为什么这个重载/命名空间/模板相关的C ++代码没有编译?

时间:2011-12-02 22:59:58

标签: c++ templates namespaces overloading

这是一些C ++代码:

namespace A {

int f(int x) { return 0; }
int f(long x) { return 1; }

template<class T> int g(T x) {
  return f(x);
}

}

namespace B {
struct C {};
}

namespace A {
int f(B::C x) { return 2; }
}

void h() {
  A::g(B::C());
}

在命名空间A中,代码声明函数f的一些重载,以及调用f的模板化函数g。然后我们在命名空间B中声明一个新类型,并在命名空间A 中为新类型重载f。使用g ++ 4.2进行编译

order.cpp: In function ‘int A::g(T) [with T = B::C]’:
order.cpp:21:   instantiated from here
order.cpp:7: error: no matching function for call to ‘f(B::C&)’
order.cpp:3: note: candidates are: int A::f(int)
order.cpp:4: note:                 int A::f(long int)

如果我执行以下任何操作,代码就会起作用:

  1. 删除名称空间。
  2. 将B :: C的重载移动到命名空间B(感谢Koenig查找)。
  3. 将B :: C的声明及其f重载移到g()的定义之上。
  4. 我对(3)感到特别困惑,因为我认为重载决议应该独立于声明的顺序。这是预期的C ++行为吗?

2 个答案:

答案 0 :(得分:6)

Clang给出了以下错误消息,它提供了一些问题的线索:

$ clang -fsyntax-only test.cc -Wall
test.cc:7:10: error: call to function 'f' that is neither visible in the
      template definition nor found by argument-dependent lookup
  return f(x);
         ^
test.cc:21:3: note: in instantiation of function template specialization
      'A::g<B::C>' requested here
  A::g(B::C());
  ^
test.cc:17:5: note: 'f' should be declared prior to the call site or in
      namespace 'B'
int f(B::C x) { return 2; }
    ^
1 error generated.

具体来说,您已经详细介绍了模板定义中依赖名称的两阶段查找。在C ++ 98中,[temp.dep.candidate]说:

  

对于依赖于模板参数的函数调用,如果函数名称是unqualified-id但不是template-id,则使用通常的查找规则(3.4.1,3.4.2)找到候选函数,除了的是:

     
      
  • 对于使用非限定名称查找(3.4.1)的查找部分,只能找到模板定义上下文中带有外部链接的函数声明。
  •   
  • 对于使用关联命名空间(3.4.2)的查找部分,只能找到在模板定义上下文或模板实例化上下文中找到的具有外部链接的函数声明。
  •   

由于找不到A::f(B::C x)使用关联的命名空间(即依赖于参数的查找),因此它必须在模板定义站点上可见,而不仅仅是在实例化时。

答案 1 :(得分:0)

例如

int f(int x) { return 0; }
int f(long x) { return 1; }

函数不是模板函数(即它们之前没有template <class T> .T是模板参数。) 因此,当达到模板化代码时,它们可以在运行中进行编译。