C++11之内联名字空间(inline namespace)和ADL特性(Argument-Dependent name Lookup)

简介: C++11之内联名字空间(inline namespace)和ADL特性(Argument-Dependent name Lookup)

场景需求

在早期C项目中,我们常常会因为命名冲突问题需要有一个“字典”来存储所用过的变量。这是因为在C语言中非静态全局变量、函数都是全局共享的。


C++就通过命名空间(也叫名字空间)来解决C语言中这个头疼的问题。实现分割全局共享的命名空间。程序员在编写代码时可以自己设置命名空间,使用者只需要通过空间名::函数/变量或者using namespace 空间名就可以使用(推荐使用前者方法)。但是,当我们空间名嵌套多层时在使用上不是很方便。


命名空间嵌套的弊端

在下面这段代码中,用户1将他的代码封装为LINXI,然后内部又进行了细分为BBCCDD。而且在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的模板类时,且类型为BBT1类型,那么代码将变得过于臃肿晦涩难懂

所以为了解决这种实际生产环境中命名空间的嵌套,导致使用上会有一定的不便。请接着往下看。

内联名字空间(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命名空间内还有cpp11cpp14cpp命名空间,这里我们使用到了__cplusplusC++的版本宏,如果当前的版本与宏的关系成立那么就将该命名空间内联到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特性缺大于优,比较鸡肋。所以我们还是在使用时还是通过::方式吧。


目录
相关文章
|
1月前
|
设计模式 安全 算法
【C++ 基础】超越边界:C++中真正不受访问修饰符限制的特性
【C++ 基础】超越边界:C++中真正不受访问修饰符限制的特性
36 0
|
1月前
|
设计模式 存储 缓存
【C++ 基本概念】深入探索C++ RTTI 特性
【C++ 基本概念】深入探索C++ RTTI 特性
61 0
|
1月前
|
算法 Java 编译器
【C++ 14新特性 】C++14 数字分隔符:深入理解与实践
【C++ 14新特性 】C++14 数字分隔符:深入理解与实践
54 2
|
1月前
|
自然语言处理 编译器 C语言
【C++ 20 新特性】参数包初始化捕获的魅力 (“pack init-capture“ in C++20: A Deep Dive)
【C++ 20 新特性】参数包初始化捕获的魅力 (“pack init-capture“ in C++20: A Deep Dive)
40 0
|
1月前
|
算法 安全 编译器
【C++20 新特性Concepts 概念】C++20 Concepts: Unleashing the Power of Template Programming
【C++20 新特性Concepts 概念】C++20 Concepts: Unleashing the Power of Template Programming
46 0
|
1月前
|
存储 算法 编译器
【C++ 内存管理 重载new/delete 运算符 新特性】深入探索C++14 新的/删除的省略(new/delete elision)的原理与应用
【C++ 内存管理 重载new/delete 运算符 新特性】深入探索C++14 新的/删除的省略(new/delete elision)的原理与应用
46 0
|
1月前
|
Linux C++ iOS开发
【C++ 17 新特性 文件管理】探索C++ Filesystem库:文件和目录操作的全面指南(二)
【C++ 17 新特性 文件管理】探索C++ Filesystem库:文件和目录操作的全面指南
253 2
|
1月前
|
Linux API C++
【C++ 17 新特性 文件管理】探索C++ Filesystem库:文件和目录操作的全面指南(一)
【C++ 17 新特性 文件管理】探索C++ Filesystem库:文件和目录操作的全面指南
320 2
|
1月前
|
算法 网络协议 编译器
【C++ 14 新特性】C++14二进制字面量:深度探索与实践
【C++ 14 新特性】C++14二进制字面量:深度探索与实践
39 1
|
27天前
|
存储 安全 编译器
【C++】类的六大默认成员函数及其特性(万字详解)
【C++】类的六大默认成员函数及其特性(万字详解)
35 3