Вопрос

В ответ на .. где -то другой вопрос я написал этот код.

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 ++, который предоставляет лучшие инструменты для написания метакода, который будет генерировать код во время компиляции?

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top