Clang-3.2可以按预期编译和编写行为:
struct have_f { int f(int i) {return 10;} }; struct empty {}; template <class T> struct outer { T t; // if T have f(),define outer_f() template<class U=decltype(t.f(1))> int outer_f(int i) { return t.f(i); } }; int main() { outer<have_f> o1; outer<empty> o2; // to silence unused var warning return o1.outer_f(10) + sizeof(o2); }
任何版本的GCC拒绝:
t.cc:13:6: error: ‘struct empty’ has no member named ‘f’ int outer_f(int i) { return t.f(i); } ^
谁是正确的? Gcc或Clang?
注意,有similar question,没有真正的答案.
解决方法
我相信这个问题是14.6.3 [temp.nondep]:
1 – Non-dependent names used in a template definition are found using the usual name lookup and bound at the point they are used.
给出的示例描述了模板定义中的形式不正确的表达“可以在模板定义中或在实例化点被诊断.
默认的template-argument(14.1p9)U = decltype(tf(1))在struct outer的实例化的上下文中是一个非依赖的名称(也就是说,它不依赖于自己模板的模板参数)所以使用T = struct为空的struct outer实例化是不正确的.该标准没有明确描述默认模板参数的评估,但唯一明确的结论是它们被视为任何其他构造,并在它们发生的时候进行评估(或者在本例中,在实例化时结构外部模板).我没有看到编译器将非依赖默认模板参数的评估延迟到SFINAE应用的上下文的任何纬度.
幸运的是,解决方案很简单:只需将默认的模板参数U作为依赖名称:
// if T have f(),define outer_f() template<class T2 = T,class U=decltype(static_cast<T2 &>(t).f(1))> int outer_f(int i) { return t.f(i); }