所以,我正在尝试做的事情并不是针对课堂朋友定义的真正意图用例。但是它确实可以在g ++下工作,而且我能说的最好是按照C ++ 14规范工作。
为了讨论的目的,clang是5.0.0而gcc是7.2.0,虽然我已经测试了其他最近和HEAD版本并得到了相同的结果。用c ++ 14标记编译的所有内容。
我感兴趣的案件的最小复制品如下。
#include <iostream>
auto foo();
template <typename T>
class Foo
{
friend auto foo()
{
return (T)3.14;
}
};
template <typename T>
void bar(T){}
int main()
{
bar(Foo<float>{});
std::cout << foo() << std::endl;
return 0;
}
Gcc行为:编译并运行。产出:3.14
Clang行为:拒绝编译。错误:'foo'
的类型冲突这本身就是完全错误的,但是当我取消Foo的模板化时,auto不会给clang任何问题,我决定尝试给foo函数一个虚拟模板。
#include <iostream>
template <typename X = int>
auto foo();
template <typename T>
class Foo
{
template <typename X>
friend auto foo()
{
return (T)3.14;
}
};
template <typename T>
void bar(T){}
int main()
{
bar(Foo<float>{});
std::cout << foo() << std::endl;
return 0;
}
Gcc行为:编译并运行。产出:3.14
Clang行为:编译并运行。输出:3
是的,当使用clang编译时,foo的类型推导完全取决于声明中的默认模板参数,尽管没有出现在定义中的任何位置。 O_O
最后,我决定尝试通过引入using指令来修复这种特殊行为。
#include <iostream>
template <typename X = int>
auto foo();
template <typename T>
class Foo
{
using Type = T;
template <typename X>
friend auto foo()
{
return (Type)3.14;
}
};
template <typename T>
void bar(T){}
int main()
{
bar(Foo<float>{});
std::cout << foo() << std::endl;
return 0;
}
Gcc行为:编译并运行。产出:3.14
Clang行为:编译器崩溃。我试过的每一个版本。
崩溃是一个明显的错误,但我对前两个例子更感兴趣。这也是铿锵的错误吗?或者是否存在一些微妙的原因导致为什么这应该是未定义的行为,而我只是对gcc感到幸运?更重要的是,任何人都可以想到一个适用于gcc和clang的解决方法吗?我正在尝试做的是检测Foo实例化的类型,前提是它在Foo之外的编译时是唯一的。
答案 0 :(得分:0)
虽然我还不完全确定为什么Clang会这样做,但我找到了一个可以在Clang和GCC上编译的解决方法。后者引发了一个警告,但它可以被抑制,或者可以使用ifdef引入轻微的定义变化,以使代码在两者上都干净地编译。
#include <iostream>
template <typename T = void>
class A
{
public:
friend auto foo(A<T>);
};
template <typename T>
class Foo
{
friend auto foo(A<void>)
{
return (T)3.14;
}
};
template <typename T>
void bar(T){}
int main()
{
bar(Foo<float>{});
std::cout << foo(A{}) << std::endl;
return 0;
}
这允许扣除foo()的返回类型,使其基于Foo实例化的模板参数,即使它们发生在不同的代码块中,这正是我所寻找的。 p>