10

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?

请注意,有类似的问题,没有真正的答案。

4

1 回答 1

9

我相信问题是14.6.3 [temp.nondep]

1 - 模板定义中使用的非依赖名称是使用通常的名称查找找到的,并在使用它们时绑定。

给出的示例描述了模板定义中的格式错误的表达式“可以在[模板定义中]或在实例化点进行诊断”。

默认模板参数(14.1p9)U=decltype(t.f(1))是实例化上下文中的非依赖名称struct outer(即,它不依赖于其自己模板的模板参数),因此它对于 with 的实例化是不正确struct outerT = 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); }
于 2012-10-10T09:11:47.620 回答