c++ - MSVC2015 中 SFINAE 成员检测的错误输出

标签 c++ templates 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

最佳答案

事实上,您的代码有错误。 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

An id-expression that denotes a non-static data member or non-static member function of a class can only be used:

  • as part of a class member access ([expr.ref]) in which the object expression refers to the member's class or a class derived from that class,

  • or to form a pointer to member ([expr.unary.op]), or

  • if that id-expression denotes a non-static data member and it appears in an unevaluated operand. [ Example:

-

 struct S {
   int m;
 };

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

— end example ]


正如我们在聊天中讨论的那样,我们得出的结论是,这两个编译器存在差异的原因是 GCC 和 MSVC 都存在阻止此代码正常运行的错误。如前所述,如果存在未正确实现规则的不相关类,MSVC 将拒绝应用 SFINAE 规则:http://rextester.com/FGLF68000

请注意,有时,更改类型特征的名称有助于 MSVC 正确解析我的代码,但它大多不可靠。

如果您希望代码按预期工作,请考虑向 Microsoft 报告错误并升级您的 GCC 版本。

关于c++ - MSVC2015 中 SFINAE 成员检测的错误输出,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/39536367/

相关文章:

c++ - 为什么 g++ 不能编译一个简单的 GTK 程序?

c++ - C++11在效率上是否比C++03快?

c++ - 如何指定 C++ 类的特定方法使用模板?

c++ - 模板化 Sum(Args...) 可变参数函数无法编译

c++ - SFINAE 的问题

c++ - decltype(..., void()) 和 void_t 的区别

c++ - 将 std::strings 插入到 std::map

c++ - 将 clang 3.1 与初始值设定项列表结合使用

c - 如何在 ANSI C 中链接模板头?

c++ - 为什么有 std::not1() 和 std::not2() 而不是单个重载的 std::not_()?