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,没有真正的答案。
答案 0 :(得分:9)
我认为问题是 14.6.3 [temp.nondep] :
1 - 使用通常的名称查找找到模板定义中使用的非依赖名称,并在使用它们时进行绑定。
给出的示例描述了模板定义“中的格式错误的表达式可以[在模板定义中]或在实例化中被诊断出来。”
默认的 template-argument (14.1p9)U=decltype(t.f(1))
是struct outer
实例化的上下文中的非依赖名称(也就是说,它不依赖于在它自己的模板的模板参数上)因此struct outer
与T = struct empty
的实例化是不正确的。该标准没有明确描述评估默认模板参数的位置,但唯一明智的结论是它们被视为任何其他构造并在它们发生时进行评估(或者,在此示例中,在实例化时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); }