В 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++, в котором четко указано, что без &
Выражение плохо сформировано:
Выражение 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
{ };