2

以下代码编译良好:

#include <cstdio>

//namespace N {
    void f( int x ) { printf( "f( int ) called\n" ); }
    void f( double x ) { printf( "f( double ) called\n" ); }
//}

template < typename T > inline void g( const T& t ) {
//    N::f( t );
    f( t ); }

struct X {};
//namespace N {
    void f( const X& x ) { printf( "f( const X& ) called\n" ); }
//}

int main() {
    g( 1 );
    g( 1.2 );
    g( X{} );
}

但是如果我在命名空间中定义 f()s,例如通过取消注释上述代码,那么编译器会生成一个错误:

t.cpp:11:5: error: no matching function for call to 'f'
    N::f( t );
    ^~~~
t.cpp:25:5: note: in instantiation of function template specialization 'g<X>' requested here
    g( X{} );
    ^
t.cpp:4:10: note: candidate function not viable: no known conversion from 'const X' to 'int' for 1st argument
    void f( int x ) { printf( "f( int ) called\n" ); }
         ^
t.cpp:5:10: note: candidate function not viable: no known conversion from 'const X' to 'double' for 1st argument
    void f( double x ) { printf( "f( double ) called\n" ); }
         ^

这是为什么?

4

1 回答 1

3

当您拥有N::f(t)时,编译器将仅查找属于命名空间成员N 在使用点之前已声明的函数,因此它不会找到采用X.

当你有 时f(t),因为函数调用是不合格的,会发生依赖参数的查找,并且作为特殊规则,模板内依赖名称的参数依赖查找也会在实例化点之前声明的关联命名空间中找到函数(即使它们是在使用点之后声明的)。当TtypeX是在全局作用域中声明的时候,全局命名空间是一个关联命名空间,这意味着依赖于参数的查找可以在全局命名空间中找到在使用点之前或之后声明的函数,只要它们是在实例化点之前声明的。的实例化点g<X>实际上是在末尾main(因为调用g<X>发生在内部main),这意味着f可以在实例化时通过依赖于参数的查找找到所有三个重载g<X>

这种用于参数相关查找的特殊查找规则对于模板库的功能是必要的。例如std::sort,它在<algorithm>某处定义并且(通常)包含在您的程序的开头附近。X您可能需要对具有重载的用户定义类型的数组进行排序operator<。通常,它的声明X和它的operator<将在#include <algorithm>指令之后,因此在定义之后std::sort。但是,我们仍然期望当std::sort调用 sort时,即使后者是在前者的定义之后X声明的,它也将能够使用operator<for 。X

于 2022-02-03T01:48:51.287 回答