Принятый ответ на этот вопрос интроспекции член-функции compiletime, хотя он по праву популярен, имеет зацепку, которая может наблюдаться в следующей программе:
#include
#include
#include
/* Here we apply the accepted answer's technique to probe for the
the existence of `E T::operator*() const`
*/
template
struct has_const_reference_op
{
template struct SFINAE {};
template static char Test(SFINAE*);
template static int Test(...);
static const bool value = sizeof(Test(0)) == sizeof(char);
};
using namespace std;
/* Here we test the `std::` smart pointer templates, including the
deprecated `auto_ptr`, to determine in each case whether
T = (the template instantiated for `int`) provides
`int & T::operator*() const` - which all of them in fact do.
*/
int main(void)
{
cout << has_const_reference_op,int &>::value;
cout << has_const_reference_op,int &>::value;
cout << has_const_reference_op,int &>::value << endl;
return 0;
}
Построено с GCC 4.6.3 , программные выходы 110
- информируя нас о том, что T = std::shared_ptr
предоставляет not , предоставить int & T::operator*() const
.
Если вы еще не разумны в этом вопросе, то посмотрите на определение std::shared_ptr
в заголовке
пролить свет. В этой реализации std::shared_ptr
происходит из базового класса, из которого он наследует operator*() const
. Таким образом, экземпляр шаблона SFINAE
, который представляет собой «поиск» оператора для U = std::shared_ptr
, не будет выполняться, поскольку std::shared_ptr
не имеет operator*()
в своем собственном праве, а создание экземпляра шаблона не «наследует».
Эта защелка не влияет на хорошо известный подход SFINAE, используя «Theofofofof () Trick», для обнаружения только того, имеет ли T
некоторая функция-член mf
(см., например, этот ответ и Комментарии). Но установление того, что T::mf
существует, часто (обычно?) Не достаточно хорошо: вам также может потребоваться установить, что у него есть желаемая подпись. Вот где проиллюстрирована техническая оценка. Указанный вариант желаемой сигнатуры вписывается в параметр типа шаблона, который должен удовлетворять &T::mf
для успешного использования зонда SFINAE. Но этот метод экземпляра шаблона дает неправильный ответ, когда T::mf
унаследован.
Безопасный метод SFINAE для интроспекции компиляции T::mf
должен избегать использования &T::mf
в аргументе шаблона для создания экземпляра типа от которого зависит разрешение шаблона функции SFINAE. Вместо этого разрешение функции шаблона SFINAE может зависеть только от точно соответствующих деклараций типа, используемых в качестве типов аргументов перегруженной функции зондирования SFINAE.
. В ответ на вопрос, который соблюдает это ограничение, я проиллюстрирую обнаружение компиляции E T::operator*() const
для произвольных T
и E
. Тот же шаблон применит mutatis mutandis для проверки любой другой сигнатуры метода участника.
#include
/*! The template `has_const_reference_op` exports a
boolean constant `value that is true iff `T` provides
`E T::operator*() const`
*/
template< typename T, typename E>
struct has_const_reference_op
{
/* SFINAE operator-has-correct-sig :) */
template
static std::true_type test(E (A::*)() const) {
return std::true_type();
}
/* SFINAE operator-exists :) */
template
static decltype(test(&A::operator*))
test(decltype(&A::operator*),void *) {
/* Operator exists. What about sig? */
typedef decltype(test(&A::operator*)) return_type;
return return_type();
}
/* SFINAE game over :( */
template
static std::false_type test(...) {
return std::false_type();
}
/* This will be either `std::true_type` or `std::false_type` */
typedef decltype(test(0,0)) type;
static const bool value = type::value; /* Which is it? */
};
В этом решении перегруженная функция зонда SFINAE test()
«вызывается рекурсивно», , (Конечно, он вообще не вызывается вообще, он просто имеет возвращаемые типы гипотетических вызовов, разрешенных компилятором.)
Нам нужно исследовать хотя бы одну и не более двух точек информации:
T::operator*()
? Если нет, мы закончили. T::operator*()
, является ли его подпись E T::operator*() const
? Мы получаем ответы, оценивая возвращаемый тип один вызов test(0,0)
. Это делается с помощью:
typedef decltype(test(0,0)) type;
Этот вызов может быть разрешен в /* SFINAE operator-exists :) */
перегрузке test()
, или он может решить перегрузку /* SFINAE game over :( */
. Он не может решить перегрузку /* SFINAE operator-has-correct-sig :) */
, потому что тот ожидает только один аргумент, и мы проходим два.
Почему мы проходим два? Просто заставить разрешение исключить /* SFINAE operator-has-correct-sig :) */
. Второй аргумент не имеет никакой другой значимости.
Этот вызов test(0,0)
разрешит /* SFINAE operator-exists :) */
, если первый аргумент 0 удовлетворяет первому типу параметра этой перегрузки, который является decltype(&A::operator*)
, с A = T
. 0 будет удовлетворять этому типу на всякий случай T::operator*
.
Предположим, что компилятор говорит «Да». Затем это происходит с /* SFINAE operator-exists :) */
, и ему нужно определить тип возврата вызова функции, который в этом случае decltype(test(&A::operator*))
- тип возврата еще одного вызова test()
.
На этот раз , мы передаем только один аргумент, &A::operator*
, который мы теперь знаем, или мы не были бы здесь. Вызов test(&A::operator*)
может разрешить либо /* SFINAE operator-has-correct-sig :) */
, либо снова, чтобы разрешить /* SFINAE game over :( */
. Вызов будет соответствовать /* SFINAE operator-has-correct-sig :) */
на всякий случай, если &A::operator*
удовлетворяет типу одного параметра этой перегрузки, который E (A::*)() const
, с A = T
.
Компилятор скажет «Да», если T::operator*
имеет эту желаемую подпись, а затем снова должен оценивать возвращаемый тип перегрузки. Больше нет «рекурсий»: это std::true_type
.
Если компилятор не выбирает /* SFINAE operator-exists :) */
для вызова test(0,0)
или не выбирает /* SFINAE operator-has-correct-sig :) */
для вызова test(&A::operator*)
, тогда в любом случае он идет с /* SFINAE game over :( */
, а конечным типом возврата является std::false_type
.
Вот тестовая программа, в которой показан шаблон, который дает ожидаемые ответы в разнообразной выборке случаев (GCC 4.6.3 снова ).
// To test
struct empty{};
// To test
struct int_ref
{
int & operator*() const {
return *_pint;
}
int & foo() const {
return *_pint;
}
int * _pint;
};
// To test
struct sub_int_ref : int_ref{};
// To test
template
struct ee_ref
{
E & operator*() {
return *_pe;
}
E & foo() const {
return *_pe;
}
E * _pe;
};
// To test
struct sub_ee_ref : ee_ref{};
using namespace std;
#include
#include
#include
int main(void)
{
cout << "Expect Yes" << endl;
cout << has_const_reference_op,int &>::value;
cout << has_const_reference_op,int &>::value;
cout << has_const_reference_op,int &>::value;
cout << has_const_reference_op::iterator,int &>::value;
cout << has_const_reference_op::const_iterator,
int const &>::value;
cout << has_const_reference_op::value;
cout << has_const_reference_op::value << endl;
cout << "Expect No" << endl;
cout << has_const_reference_op::value;
cout << has_const_reference_op,char &>::value;
cout << has_const_reference_op,int const &>::value;
cout << has_const_reference_op,int>::value;
cout << has_const_reference_op,int &>::value;
cout << has_const_reference_op::value;
cout << has_const_reference_op,int &>::value;
cout << has_const_reference_op,int &>::value;
cout << has_const_reference_op::value;
cout << has_const_reference_op::value << endl;
return 0;
}
Есть ли новые недостатки в этой идее? Может ли он стать более общим без лишних падений, чтобы избежать этого?
Это - эффект новой возможности в Vista под названием "Упаковка": Windows имеет несколько механизмов, которые позволяют пользователю/администратору настраивать приложения для автоматического выполнения, когда окна запускаются. Эта функция главным образом используется для одной из этих целей: 1. Программы, которые являются частью основной рабочей среды пользователя, такого, что первое действие пользователь обычно брало бы при запуске компьютера, должны запустить их. 2. Все виды фона "агенты" - скайп, средство рассылки, winamp и т.д.
Когда слишком многие (или слишком тяжелый) программы регистрируются для работы запуска, конечный результат состоит в том, что пользователь ничего не может на самом деле сделать в течение первых нескольких секунд/минуты после входа в систему, который может быть действительно раздражающим. В прибывает функция "Boxing" Vista:
Кратко, Vista вынуждает все программы, вызванные через ключ Выполнения работать в низком приоритете в течение первых 60 секунд после входа в систему. Это влияет на оба приоритета ввода-вывода (который установлен на Очень Низкий), и приоритет ЦП. Очень Низкий приоритет запросы ввода-вывода не проходит через кэш файла, но переходит непосредственно к диску. Таким образом они намного медленнее, чем регулярный ввод-вывод. Продолжительность периода упаковки установлена значением реестра: "HKLM\Software\Microsoft\Windows\CurrentVersion\Explorer\Advanced\DelayedApps\Delay_Sec".
Для программы, вероятно, нужна еще некоторая информация, помещенная в ее свойства. Это должно "Работать Как" вместо просто выполнения.
Возможно, это приложение должно быть разработано как услуга вместо программы, которая будет запущена, или у Вас мог быть сервис, который запускает программу когда его решительное лучший удобный момент.