g++ 和 clang++ SFINAE 和 SFINAE 失败的不同行为
g++ and clang++ different behaviour with SFINAE and SFINAE failure
C++11 专家的几个问题。
我正在与 SFINAE 打交道,我遇到了一个奇怪的情况,其中 g++ (4.9.2) 和 clang++ (3.5.0) 的行为不同。
我准备了以下示例代码。很抱歉,我无法做到更简洁。
#include <string>
#include <iostream>
#include <typeinfo>
#include <type_traits>
template <typename X>
class foo
{
private:
template <typename R>
using enableIfIsInt
= typename std::enable_if<std::is_same<X, int>::value, R>::type;
public:
foo ()
{ }
template <typename R = void>
enableIfIsInt<R> bar ()
{ std::cout << "bar: is int\n"; }
void bar ()
{
std::cout << "bar: isn't int; is [" << typeid(X).name() << "]{"
<< typeid(enableIfIsInt<void>).name() << "}\n";
}
};
int main ()
{
foo<long> fl;
foo<int> fi;
fl.bar();
fi.bar();
return 0;
}
我的想法是创建一个模板 foo<X>
class,它(通过 SFINAE)可以根据 X
模板参数以一种或另一种方式定义方法。
程序在 g++ 4.9.2 下编译良好,但在 clang++ 3.5.0 下出现以下错误
test.cpp:13:36: error: no type named 'type' in
'std::__1::enable_if<false, void>'; 'enable_if' cannot be used to disable
this declaration
= typename std::enable_if<std::is_same<X, int>::value, R>::type;
^~~~~~~~~~~~~~~~~~~~~~~~~~~
test.cpp:26:23: note: in instantiation of template type
alias 'enableIfIsInt' requested here
<< typeid(enableIfIsInt<void>).name() << "}\n";
^
test.cpp:36:7: note: in instantiation of member function
'foo<long>::bar' requested here
fl.bar();
^
1 error generated.
我想 clang++ 是对的,但我对 C++11 专家的第一个问题是:谁是对的? g++ 还是 clang++?
关于g++产生的程序输出,如下
bar: isn't int; is [i]{v}
所以 g++ 似乎忽略了 fl.bar();
指令。
现在稍微改动一下:我把foo<X>::bar()
的第二个版本修改成这样
void bar ()
{ std::cout << "bar: isn't int; is [" << typeid(X).name() << "]\n"; }
删除std::enable_if
里面的函数可恶。现在 g++ 和 clang++ 都可以毫无问题地编译,并且程序的两个编译版本的输出都是
bar: isn't int; is [l]
bar: isn't int; is [i]
所以,我的第二个问题是:我做错了什么?为什么在 int
的情况下,我没有获得 foo<X>::bar()
的 "is int"
版本?
如果我在做一些傻事,请耐心等待:我正在努力学习 C++11。
抱歉我的英语不好。
clang 的错误不是来自替换失败。它来自这里:
void bar ()
{
std::cout << "bar: isn't int; is [" << typeid(X).name() << "]{"
<< typeid(enableIfIsInt<void>).name() << "}\n"; // <==
}
enableIfIsInt<void>
不在直接上下文中,这是一个严重的失败,因为 X
不是 int
。您根本不能在该上下文中使用该表达式。
一旦你删除它 - 非模板 bar()
总是被调用。那是因为这两个函数是等价匹配,并且在重载决策中非模板优先于模板。
所以真正的解决方案是使用标签调度:
void bar() { bar(std::is_same<X, int>{}); }
void bar(std::true_type ) {
std::cout << "bar: is int\n";
}
void bar(std::false_type ) {
std::cout << "bar: isn't int; is [" << typeid(X).name() << "]\n";
}
两个编译器都愉快地产生:
bar: isn't int; is [l]
bar: is int
C++11 专家的几个问题。
我正在与 SFINAE 打交道,我遇到了一个奇怪的情况,其中 g++ (4.9.2) 和 clang++ (3.5.0) 的行为不同。
我准备了以下示例代码。很抱歉,我无法做到更简洁。
#include <string>
#include <iostream>
#include <typeinfo>
#include <type_traits>
template <typename X>
class foo
{
private:
template <typename R>
using enableIfIsInt
= typename std::enable_if<std::is_same<X, int>::value, R>::type;
public:
foo ()
{ }
template <typename R = void>
enableIfIsInt<R> bar ()
{ std::cout << "bar: is int\n"; }
void bar ()
{
std::cout << "bar: isn't int; is [" << typeid(X).name() << "]{"
<< typeid(enableIfIsInt<void>).name() << "}\n";
}
};
int main ()
{
foo<long> fl;
foo<int> fi;
fl.bar();
fi.bar();
return 0;
}
我的想法是创建一个模板 foo<X>
class,它(通过 SFINAE)可以根据 X
模板参数以一种或另一种方式定义方法。
程序在 g++ 4.9.2 下编译良好,但在 clang++ 3.5.0 下出现以下错误
test.cpp:13:36: error: no type named 'type' in
'std::__1::enable_if<false, void>'; 'enable_if' cannot be used to disable
this declaration
= typename std::enable_if<std::is_same<X, int>::value, R>::type;
^~~~~~~~~~~~~~~~~~~~~~~~~~~
test.cpp:26:23: note: in instantiation of template type
alias 'enableIfIsInt' requested here
<< typeid(enableIfIsInt<void>).name() << "}\n";
^
test.cpp:36:7: note: in instantiation of member function
'foo<long>::bar' requested here
fl.bar();
^
1 error generated.
我想 clang++ 是对的,但我对 C++11 专家的第一个问题是:谁是对的? g++ 还是 clang++?
关于g++产生的程序输出,如下
bar: isn't int; is [i]{v}
所以 g++ 似乎忽略了 fl.bar();
指令。
现在稍微改动一下:我把foo<X>::bar()
的第二个版本修改成这样
void bar ()
{ std::cout << "bar: isn't int; is [" << typeid(X).name() << "]\n"; }
删除std::enable_if
里面的函数可恶。现在 g++ 和 clang++ 都可以毫无问题地编译,并且程序的两个编译版本的输出都是
bar: isn't int; is [l]
bar: isn't int; is [i]
所以,我的第二个问题是:我做错了什么?为什么在 int
的情况下,我没有获得 foo<X>::bar()
的 "is int"
版本?
如果我在做一些傻事,请耐心等待:我正在努力学习 C++11。
抱歉我的英语不好。
clang 的错误不是来自替换失败。它来自这里:
void bar ()
{
std::cout << "bar: isn't int; is [" << typeid(X).name() << "]{"
<< typeid(enableIfIsInt<void>).name() << "}\n"; // <==
}
enableIfIsInt<void>
不在直接上下文中,这是一个严重的失败,因为 X
不是 int
。您根本不能在该上下文中使用该表达式。
一旦你删除它 - 非模板 bar()
总是被调用。那是因为这两个函数是等价匹配,并且在重载决策中非模板优先于模板。
所以真正的解决方案是使用标签调度:
void bar() { bar(std::is_same<X, int>{}); }
void bar(std::true_type ) {
std::cout << "bar: is int\n";
}
void bar(std::false_type ) {
std::cout << "bar: isn't int; is [" << typeid(X).name() << "]\n";
}
两个编译器都愉快地产生:
bar: isn't int; is [l]
bar: is int