MSVC2015 中 SFINAE 成员检测的错误输出
Wrong output for SFINAE member detect in MSVC2015
我正在学习 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
,为什么?
编辑:
据称违反 c++ 语法的工作 GCC 5.3.0 代码的其他示例:
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++ 标准中明确指出没有 &
的部分,表达式的格式不正确:
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 都存在阻止此代码正常运行的错误。如前所述,如果存在未正确实施规则的无关 class,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
{ };
我正在学习 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
,为什么?
编辑:
据称违反 c++ 语法的工作 GCC 5.3.0 代码的其他示例:
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++ 标准中明确指出没有 &
的部分,表达式的格式不正确:
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 都存在阻止此代码正常运行的错误。如前所述,如果存在未正确实施规则的无关 class,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
{ };