2016-09-16 4 views
2

Я узнал о SFINAE и о том, как легко реализовать его с помощью void_t. Но я получаю различные результаты для различных компиляторов:Неверный выход для определения члена SFINAE в MSVC2015

//pre c++17 void_t definition: 
template<class... Ts> struct make_void {typedef void type;}; 
template<class... Ts> using void_t = typename make_void<Ts...>::type; 

//check for member helper structures 
template<class, class = void> 
struct has_abc : std::false_type 
{ }; 

template<class T> 
struct has_abc<T, void_t<decltype(T::abc)>> : std::true_type 
{ }; 

class has 
{ 
public: 
    void abc(); 
}; 

class has_not 
{ }; 

int main() 
{ 
    std::cout << has_abc<has>::value << std::endl; 
    std::cout << has_abc<has_not>::value << std::endl; 
} 

GCC 5.3.0 печатает ожидаемый результат 1 0, но MSVC 2015 печатает 0 0, почему?


EDIT:

Дополнительный пример с рабочим GCC 5.3.0 код, который предположительно нарушает C++ синтаксис:

template<class T> 
void test() 
{ 
    std::cout << std::is_same<decltype(T::func), void(T::*)(void)>::value << std::endl; 
} 

class Test 
{ 
public: 
    void func(); 
}; 

int main() 
{ 
    test<Test>(); 
} 

Выход:

1 
+0

Просто любопытно, почему бы вам не определить 'void_t' как:' шаблон с использованием void_t = аннулируются; 'В чем проблема с этим? – Nawaz

+0

@Nawaz Некоторые компиляторы замыкают его на 'void' и избегают всех экземпляров типа. – Barry

+0

@ Барри: Я тоже так думал. Но разве это разрешено спецификацией? Что касается * какой версии * спецификации, это будет интересный вопрос! – Nawaz

ответ

2

На самом деле есть ошибка с вашим кодом. MSVC прав, тогда как GCC просто ошибается.

Синтаксис для указателя на функцию-член не работает так. Вы есть поставить & перед выражением:

//check for member helper structures 
template<class, class = void> 
struct has_abc : std::false_type {}; 

template<class T> 
struct has_abc<T, void_t<decltype(&T::abc)>> : std::true_type {}; 
//  Notice the '&' there ------^ 

Синтаксис T::member работать только со статическим элементом данных, и typename T::member работы для типов членов. При работе с sfinae важно различать небольшие синтаксические свойства и различия.

как запрос в комментариях, вот множественное утверждение, что показывает, что не статический член не может ссылаться без & с GCC 5.3: https://godbolt.org/g/SwmtG2

Вот пример с GCC: http://coliru.stacked-crooked.com/a/0ee57c2c34b32753

Вот пример с MSVC: http://rextester.com/FJH22266

Вот раздел из стандарта C++, которые явным образом заявить, что без &, выражение не очень хорошо образован:

[expr.prim.id]/2

идентификатор-выражение, которое обозначает нестатический элемент данных или не статический член функции класса может быть использовано только:

  • как часть доступа члена класса ([expr.ref]), в котором выражение объект относится к классу участника или класса, производного от этого класса,

  • или сформировать указатель на член ([expr.unary.op]), или

  • если это id-выражение обозначает нестатический элемент данных и отображается в неоцененном операнде.[Пример:

-

struct S { 
    int m; 
}; 

int i = sizeof(S::m);   // OK 
int j = sizeof(S::m + 42);  // OK 

- конец Пример]


Как мы обсуждали в чате, мы пришли к выводу, что причина есть разница между двумя компиляторами заключается в том, что как GCC, так и MSVC имеют ошибки, которые предотвращают это код для работы. Как упоминалось, MSVC откажется применять правило SFINAE, если существует неродственный класс, который не правильно реализует правило: http://rextester.com/FGLF68000

Отметим, что когда-то, изменив имя типа, помогло MSVC правильно разобрать мой код, но это в основном ненадежное.

Рассмотрите сообщение об ошибке на microsoft и обновите версию GCC, если вы хотите, чтобы ваш код работал должным образом.

+0

Wat? Как это что-то меняет. Еще один вывод, и я уверен, что вы можете обращаться к функции-члену без '&' - это просто класс, а не вызов, это разные. –

+0

К нисходящей: мой ответ правильный. См. Мои правки. –

+0

@EbisuDev. Пожалуйста, посетите приведенный мной пример и посмотрите результат самостоятельно. –

0

этот способ работает:

template<class T> 
struct has_abc<T, void_t<decltype(std::declval<T>().abc())>> : std::true_type 
{ }; 
+0

Рабочее решение, но не будет работать для членов, у которых нет 'operator()', например встроенных типов. –

Смежные вопросы