如果在演绎期间无法解析函数的地址,是SFINAE还是编译器错误?

Fai*_*ali 23 c++ templates sfinae overload-resolution c++11

在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);  

}
Run Code Online (Sandbox Code Playgroud)

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

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

FCD-C++ 0x是否清楚地指明了这里应该发生什么?
编译器拒绝此代码是错误的吗?
扣除的"直接背景"是否需要更好地定义?

谢谢!

Joh*_*itb 29

template<class T> void f(T, 
    typename size_map<sizeof(&U::foo)>::type* = 0); 
Run Code Online (Sandbox Code Playgroud)

这不起作用,因为U不参与演绎.虽然U是一种依赖类型,但在推断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); 
Run Code Online (Sandbox Code Playgroud)

所以在你的情况下因为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); 
Run Code Online (Sandbox Code Playgroud)

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

  • 请注意,如果它有帮助:如果没有成员函数“f”的“U1”模板参数,如答案中所述,当实例化类“S&lt;X&gt;”时,编译器将创建“f”的声明,但是函数“f”本身不会被实例化,直到它在某个地方被实际调用为止。因为 DECLARATION 的创建**不是** OVERLOAD RESOLUTION,SFINAE *不*适用,但编译器仍然查看它创建的声明来尝试解析 `X::foo` (因此会失败,因为 SFINAE 确实不适用)。 (2认同)