C++11之内联名字空间(inline namespace)和ADL特性(Argument-Dependent name Lookup)
Posted 林夕07
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++11之内联名字空间(inline namespace)和ADL特性(Argument-Dependent name Lookup)相关的知识,希望对你有一定的参考价值。
系列文章
C++11之正则表达式(regex_match、regex_search、regex_replace)
C++11之线程库(Thread、Mutex、atomic、lock_guard、同步)
C++11之智能指针(unique_ptr、shared_ptr、weak_ptr、auto_ptr)浅谈内存管理
C++11之强制类型转换(static_cast,const_cast,dynamic_cast,reinterpret_cast)
C++11之右值引用:移动语义和完美转发(带你了解移动构造函数、纯右值、将亡值、右值引用、std::move、forward等新概念)
C++11之用户自定义字面量(ClassType operator““_C(param…))
目录
场景需求
在早期C项目中,我们常常会因为命名冲突问题需要有一个“字典”来存储所用过的变量。这是因为在C语言中非静态全局变量、函数都是全局共享的。
C++就通过命名空间(也叫名字空间)来解决C语言中这个头疼的问题。实现分割全局共享的命名空间。程序员在编写代码时可以自己设置命名空间,使用者只需要通过空间名::函数/变量
或者using namespace 空间名
就可以使用(推荐使用前者方法)。但是,当我们空间名嵌套多层时在使用上不是很方便。
命名空间嵌套的弊端
在下面这段代码中,用户1将他的代码封装为LINXI
,然后内部又进行了细分为BB
、CC
、DD
。而且在DD
空间下使用了BB
的类型。
#include <iostream>
using namespace std;
namespace LINXI
namespace BB
class T1
public:
T1()
cout << "T1 is BB" << endl;
;
namespace CC
template<class T>
class T2
;
namespace DD
BB::T1 t1;
class T1
public:
T1()
cout << "T1 in DD" << endl;
;
T1 t2;
BB::T1 t3;
int main()
LINXI::CC::T2<LINXI::BB::T1> t;
return 0;
那么当我们需要创建一个LINXI空间下的CC
的模板类时,且类型为BB
的T1
类型,那么代码将变得过于臃肿且晦涩难懂。
所以为了解决这种实际生产环境中命名空间的嵌套,导致使用上会有一定的不便。请接着往下看。
内联名字空间(inline namespace)
在C++11中引入了内联命名空间,可以通过inline namespace
声明一个内联的命名空间。
作用:内联命名空间可以让程序员在父命名空间定义或特化子命名空间的模板。
通过内联命名空间优化嵌套问题
我们只需要在之前的基础上将BB子命名空间和CC子命名空间改成内联命名空间,然后我们就可以很简单的实现上述的操作。
#include <iostream>
using namespace std;
namespace LINXI
inline namespace BB
class T1
public:
T1()
cout << "T1 is BB" << endl;
;
class T3 ;
inline namespace CC
template<class T>
class T2 ;
namespace DD
T1 b; // T1 is BB
struct T1
T1()
cout << "T1 is DD" << endl;
;
T1 t1; // T1 is DD
BB::T1 t2; // T1 is BB
当我们使用特例化时就需要在LINXI的命名空间下进行实现。
namespace LINXI
template<>
class T2<T1>; // 特例化
创建一个T1
模板的类T2
的对象我们可以通过下面俩种方法实现:
using namespace LINXI;
T2<T1> t;
LINXI::T2<LINXI::T1> t;
运行结果:
T1 is BB
T1 is DD
T1 is BB
这也有缺点会使
BB
的命名空间形如虚设,使得命名空间的分割性就失去了。
内联命名空间配合宏使用
在下面这段代码中,LINXI
命名空间内还有cpp11
、cpp14
、cpp
命名空间,这里我们使用到了__cplusplus
C++的版本宏,如果当前的版本与宏的关系成立那么就将该命名空间内联到LINXI
中。
#include <iostream>
using namespace std;
// 201103L(C++11) 201402L(C++14), 201703L(C++17), or 202002L(C++20)
namespace LINXI
#if __cplusplus == 201103L
inline
#endif
namespace cpp11
class AA
public:
AA()
cout << "AA is C++11" << endl;
;
#if __cplusplus == 201402L
inline
#endif
namespace cpp14
class AA
public:
AA()
cout << "AA is C++14" << endl;
;
#if __cplusplus < 201103L
inline
#endif
namespace cpp
class AA
public:
AA()
cout << "AA is C++" << endl;
;
int main()
using namespace LINXI;
AA a; // 默认版本 C++
cpp11::AA a11; // 强制使用C++11
cpp14::AA a14; // 强制使用C++14
return 0;
运行结果
AA is C++
AA is C++11
AA is C++14
**优点:**对于长期需要维护的项目,在版本迭代更新时非常的方便。
ADL特性(Argument-Dependent name Lookup)
作用:ADL是允许编译器在命名空间内找不到函数名称时,会在参数的命名空间中继续进行查找函数命名。
下面这段代码中,函数ADLFunction
就不需要在使用中声明自己的命名空间,因为编译器可以在参数a
的命名空间中找到ADL
,编译器也就可以成功识别了。
#include <iostream>
using namespace std;
namespace ADL
struct A;
void ADLFunction(A a);
int main()
ADL::A a;
ADLFunction(a);
return 0;
可以看到我们没有写完参数时,编译器一直提醒我们添加ADL
命名空间。
在写完之后,编译器已经可以识别这个函数了。并且可以正常运行。
总结
ADL带来了一定的便捷性,但也破坏了命名空间的封装性,而且更多人觉得ADL特性缺大于优,比较鸡肋。所以我们还是在使用时还是通过::
方式吧。
以上是关于C++11之内联名字空间(inline namespace)和ADL特性(Argument-Dependent name Lookup)的主要内容,如果未能解决你的问题,请参考以下文章