23

在 C++0x 中,SFINAE 规则已被简化,因此在演绎的“直接上下文”中出现的任何无效表达式或类型都不会导致编译器错误,而是会导致演绎失败 (SFINAE)。

我的问题是:
如果我取了一个重载函数的地址并且它不能被解析,那是在直接推理的上下文中失败吗?
(即如果无法解决,是硬错误还是 SFINAE)?

这是一些示例代码:

struct X
{
  // template<class T> T* foo(T,T); // lets not over-complicate things for now
  void foo(char);
  void foo(int);
};


template<class U> struct S
{
  template<int> struct size_map 
  { typedef int type; };


// here is where we take the address of a possibly overloaded function
  template<class T> void f(T, 
      typename size_map<sizeof(&U::foo)>::type* = 0); 


  void f(...);
};

int main()
{
  S<X> s;

// should this cause a compiler error because 'auto T = &X::foo' is invalid?
  s.f(3);  

}

Gcc 4.5 声明这是一个编译器错误,并且 clang 吐出了一个断言违规。

以下是一些更相关的感兴趣的问题:

FCD-C++0x 是否清楚地指定了这里应该发生什么?
编译器拒绝此代码是否错误?
是否需要更好地定义演绎的“直接上下文”?

谢谢!

4

1 回答 1

29
template<class T> void f(T, 
    typename size_map<sizeof(&U::foo)>::type* = 0); 

这个不行,因为U不参与扣分。WhileU是一个依赖类型,在推演过程中,f它被视为使用非依赖名称拼写的固定类型。您需要将其添加到参数列表中f

/* fortunately, default arguments are allowed for 
 * function templates by C++0x */
template<class T, class U1 = U> void f(T, 
    typename size_map<sizeof(&U1::foo)>::type* = 0); 

因此,在您的情况下,因为U::foo不依赖于f自身的参数,所以在隐式实例化时会收到错误S<X>(尝试注释掉调用,它仍然应该失败)。FCD 在14.7.1/1

类模板特化的隐式实例化导致类成员函数、成员类、静态数据成员和成员模板的声明的隐式实例化,而不是定义或默认参数的隐式实例化;

也就是说,如果你隐式实例化S<X>下面的函数模板声明将被实例化

template<class T> void S<X>::f(T, 
  typename size_map<sizeof(&X::foo)>::type* = 0); 

然后对该模板声明的分析将发现它无法解析对的引用X::foo并出错。如果添加U1,则模板声明将不会尝试解析对U1::foo(因为U1是 的参数)的引用,因此在尝试调用f时将保持有效和 SFINAE 。f

于 2010-05-23T14:38:08.283 回答