两种参数类型_C++20 新特性(23):模板函数的参数依赖查找

C++有两种函数查找方式:

  1. 普通查找:从当前命名空间开始,逐层往上一层命名空间进行查找,直到最外层命名空间
  2. 参数依赖查找(ADL):又名 Koenig 查找,用于函数查找,支持在函数的参数所在的命名空间中进行查找

C++20 对参数依赖查找进行进一步的增强,支持对模板函数的参数依赖查找。下面先回顾一下普通的参数依赖查找,再看新的模板函数的参数依赖查找是如何进行的,以及由此引起的副作用和规避方法。

b77dea449c960d695a3e237ec823e299.png

普通参数依赖查找,ADL

普通参数依赖查找,是根据参数类型的命名空间进行查找,只会查找参数类型所在的命名空间,不会再往上一级命名空间进行查找。例如:

#include using std::cout, std::endl;namespace A1{    namespace A2    {        struct X { int m_a { 5 }; };        int func_a( struct X a )        {            cout << "A1::A2::func_a()" << endl;            return a.m_a + 1;        }    };    int func_b( struct A2::X a )    {        cout << "A1::func_b()" << endl;        return a.m_a + 1;    }};int main( int argc, char * argv[] ){    struct A1::A2::X a { 7 };    func_a( a );  // <1> ADL 查找,根据参数 a 所在命名空间 A1::A2 ,找到函数 A1:A2::func_a()//  func_b( a );  // <2> Error,因为 ADL 查找只会在参数所在的命名空间进行查找,不会再往上一级命名空间查找    A1::func_b( a );    return 0;}

另外,参数依赖查找和普通查找是并列的同时都会去找,并不是说普通查找更优先。例如上面的例子,如果最外层的全局命名空间下也定义了函数: int func_a( struct A1::A2::X a ); 那么 <1> 语句会编译错误,编译器无法区分是使用全局命名空间下的 func_a 函数还是 A1::A2 命名空间下面的 func_a 函数,此时需要使用 ::func_a 或者 A1::A2::func_a 的方式来指定调用哪一个。

模板函数的参数依赖查找

模板函数的参数依赖查找和普通函数的参数依赖查找一样,都是根据调用时的参数,在函数参数类型所在的命名空间中查找。

但模板函数和普通函数的一个差别,是调用模板函数时,可以通过 func( ... ) 的形式在模板函数名称后面加上类型,表示具体的特化。但这个模板参数类型,是不会进行参数依赖查找的。

下面是一个例子代码及说明:

#include #include using std::cout, std::endl;int h = 0;int g ( )  {   return 3;  }namespace N{    struct A    {        int m_a { 4 };    };    template < typename T > int f ( const T & a ) { return 4; }    template < typename T > int g ( const T & a ) { return 5; }    template < typename T > int h ( const T & a ) { return 6; }    template < typename T > int k ( const T & a ) { return 7; }    int k ( const struct A & a ) { return 8; }    template < typename T > int k () { return 9; }};int main( int argc, char * argv[] ){    int a1 = f< N::A > ( N::A() );   // <1> 参数依赖查找,从命名空间 N 中进行查找    int a2 = g< N::A > ( N::A() );  // <2> 虽然 g 也有全局函数,但类型不对,所以会根据参数继续再命名空间 N 中查找//  int a3 = h< N::A > ( N::A() );  // <3> Error,对 h 的查找首先命中最外层的全局变量 h ,就不会再去查找函数,    int a4 = k( N::A() );                 // <4> 普通的参数依赖查找,优先匹配非模块函数    int a5 = k< N::A > ( N::A() );  // <5> 指定使用模板函数//  int a6 = k< N::A > ( );           // <6> Error,模板参数本身不进行参数依赖查找,因此不会找命名空间 N 中的函数    cout << a1 << " " << a2 << " " << a4 << " " << a5 << endl;   // 输出 4 5 8 7    return 0;}

副作用和规避方法

由于模板的尖括号同时也是小于运算符,因此会出现二义性。因此在C++标准中,规定了如果一个名称被认为是函数名称,那么他后面的 '

下面是一个例子说明:

struct B{    int m_b { 5 };};int g ( )  {   return 3;  }bool operator < ( int (*fp)(), B b ){    return b.m_b > 0;}   B b1;//  bool c1 = g < b1;     // <1> Error,g 是函数名,后面的 < 符号优先解析成表示模板bool c2 =  ( g ) < b1;    // <2> 加一个括号可以规避,这时候 < 符号解析成小于运算符

【往期回顾】

C++20 新特性(22):C++ attribute的改进

C++20 新特性(21):其他const相关的改进

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值