Ошибки с использованием decktype () и sfinae
Вопрос
В ответ на .. где -то другой вопрос я написал этот код.
struct no_type{};
template<typename T> struct has_apply {
static decltype(T().apply<0u>(double())) func( T* ptr );
static no_type func( ... );
static const bool result = !std::is_same<no_type, decltype(func(nullptr))>::value;
};
class A {
public:
template< unsigned n >
void apply( const double& );
};
class B {
};
int main()
{
std::cout << std::boolalpha << has_apply< A >::result << '\n';
std::cout << std::boolalpha << has_apply< B >::result << '\n';
std::cin.get();
return( 0 );
}
Теперь мне кажется, что результат должен быть правдой, если T предлагает нестатическую функцию члена «применить», которая принимает двойной RValue и параметр шаблона, и в противном случае False. Однако приведенный пример фактически не собирается компиляция для класса B, при составлении has_apply<B>
. Анкет Разве не должен тот факт, что замена T не удалась в заявлении Decltype, что он просто вызывает другой фанк? Разве это не в том смысле, что Sfinae?
Решено в самой смешной, бессмысленной моде:
struct no_type{};
template<typename T> struct has_apply {
template<typename U> static decltype(U().apply<0u>(double())) func( U* );
template<typename U> static no_type func( ... );
static const bool result = !std::is_same<no_type, decltype(func<T>(nullptr))>::value;
};
class A {
public:
template< unsigned n >
void apply( const double& );
};
class B {
};
int main()
{
std::cout << std::boolalpha << has_apply< A >::result << '\n';
std::cout << std::boolalpha << has_apply< B >::result << '\n';
std::cin.get();
return( 0 );
}
Решение
SFINAE применяется, когда замена не удается для параметра шаблона функции шаблона, а не для параметра шаблона класса, который выполняет (не Tatemberate) функцию, которая, как оправдан, как и в вашем случае.
После исправления, вы должны хотя бы измениться decltype(T().apply<0u>(double()))
к decltype(T().template apply<0u>(double()))
так как T()
Выражение имеет зависимый тип. Причина этого в следующем: когда компилятор впервые видит T().apply<0u>
, это ничего не знает о T
Тем не менее, как это должно проанализировать токены apply
и <
после .
? apply
может быть шаблоном участника, а затем <
Начнет список аргументов для этого. Ото apply
Вместо этого может быть членом неэлемента (например, член данных), а затем <
будет проанализирован как оператор «менее чем». Есть двусмысленность, и компилятор все еще слишком рано разрешить это на данный момент. Существует необходимость в механизме устранения неоднозначности, который программист мог бы использовать, чтобы рассказать компилятору, что apply
Ожидается, что будет: шаблон или нет. И вот приходит .template
(или ->template
, или ::template
) Построить к спасению: если оно присутствует, компилятор знает, что это должен быть член шаблона, в противном случае, если его нет, то компилятор знает, что участник не должен быть шаблоном.
Наконец, вот пример, который я создал, который работает правильно и дает желаемые результаты на G ++ 4.5.0 с -std=c++0x
:
#include <iostream>
template < class T >
decltype( T().template apply< 0u >( double() ) ) f( T &t )
{
return t.template apply< 0u >( 5. );
}
const char *f( ... )
{
return "no apply<>";
}
class A {
public:
template < unsigned >
int apply( double d )
{
return d + 10.;
}
};
class B {};
int main()
{
A a;
std::cout << f( a ) << std::endl;
B b;
std::cout << f( b ) << std::endl;
}
Вывод:
15
no apply<>
Теперь, если вы удалите оба .template
с самого начала f()
Определение, тогда вывод становится:
no apply<>
no apply<>
Который должен указывать на смену замены на class A
так как у него нет не Tatemplate Член имени apply
. Анкет Sfinae в действии!
Другие советы
Извините за публикацию в качестве ответа, но комментарии, кажется, предназначены для меня.
Я видел людей, комментирующих Declval (), но это не нужно. Вместо t () можно просто написать
decltype( *(T*)0->template apply< 0u >( double() ) ) )
Что работает, поскольку он появляется только внутри Decltype и фактически не оценивается во время выполнения. Другим вариантом будет объявить
T T_obj( void );
а затем используйте
decltype( T_obj().template apply< 0u >( double() ) ) )
В качестве побочного комментария я вспоминаю, как читал, что Stroustrup разработал язык, потому что он не хотел делать работу с неправильными инструментами. Разве C ++ не является неправильным языком для метапрограммирования?
В то время как C ++ 0x улучшает ситуацию, это не кажется фокусом. Есть ли какой-либо другой язык как «близкий к металлу» как C ++, который предоставляет лучшие инструменты для написания метакода, который будет генерировать код во время компиляции?