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 intn"; }
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>
类,该类(通过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++还是叮当?
关于g++生成的程序输出,它是以下
bar: isn't int; is [i]{v}
因此g++似乎忽略了CCD_ 3指令。
现在有一点变化:我用这种方式修改了foo<X>::bar()
的第二个版本
void bar ()
{ std::cout << "bar: isn't int; is [" << typeid(X).name() << "]n"; }
删除函数obiot内的CCD_ 5。现在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 intn";
}
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
相关文章:
- 为什么使用SFINAE而不是函数重载
- 如何使用模板函数的函数签名进行SFINAE
- 数据成员SFINAE的C++17测试:gcc vs clang
- 使用在用于SFINAE的void_t中具有参数的方法
- 编译器如何在使用SFINAE的函数和标准函数之间确定两者是否可行
- 提供与TMP和SFINAE的通用接口
- 不明白迭代器,引用和指针失效,一个例子
- "Inverse SFINAE"避免模棱两可的过载
- 表达式 SFINAE:如何根据类型是否包含具有一个或多个参数的函数来选择模板版本
- 如何在儿童类中使用SFINAE
- 使用 SFINAE 作为模板参数的编译时递归
- 使用 SFINAE 设计模板方法
- C++,您能否设计一种数据结构,将指针保存在连续内存中并且不会使它们失效?
- 与SFINAE支票交朋友
- std::unordered_map::提取引用/指针失效
- C++许多 SFINAE 风格的过载
- 是否可以混合使用SFINAE和模板专业化?
- C++表达SFINAE和ostream操纵器
- SFINAE不能防止模棱两可的操作员过载吗?
- g++和clang++在SFINAE和SFINAE失效时的不同行为