В MSVC2015 обнаружен неправильный вывод для члена SFINAE

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

//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, Зачем?


РЕДАКТИРОВАТЬ:

Дополнительный пример с работающим кодом 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

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

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

  • как часть доступа к члену класса ([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, если вы хотите, чтобы ваш код работал должным образом.

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

template<class T>
struct has_abc<T, void_t<decltype(std::declval<T>().abc())>> : std::true_type
{ };
Другие вопросы по тегам