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++ 20新特性之Concepts
在C++ 20之前,我们在编写泛型代码时,模板参数的约束往往通过复杂的SFINAE(Substitution Failure Is Not An Error)策略或繁琐的Traits类来实现。这不仅难以阅读,也非常容易出错,导致很多程序员在提及泛型编程时,总是心有余悸、脊背发凉。 在没有引入Concepts之前,我们只能依靠经验和技巧来解读编译器给出的错误信息,很容易陷入“类型迷路”。这就好比在没有GPS导航的年代,我们依靠复杂的地图和模糊的方向指示去一个陌生的地点,很容易迷路。而Concepts的引入,就像是给C++的模板系统安装了一个GPS导航仪
103 59
|
1月前
|
存储 编译器 C++
【C++】面向对象编程的三大特性:深入解析多态机制(三)
【C++】面向对象编程的三大特性:深入解析多态机制
|
1月前
|
存储 编译器 C++
【C++】面向对象编程的三大特性:深入解析多态机制(二)
【C++】面向对象编程的三大特性:深入解析多态机制
|
1月前
|
编译器 C++
【C++】面向对象编程的三大特性:深入解析多态机制(一)
【C++】面向对象编程的三大特性:深入解析多态机制
|
1月前
|
存储 安全 编译器
【C++】C++特性揭秘:引用与内联函数 | auto关键字与for循环 | 指针空值(一)
【C++】C++特性揭秘:引用与内联函数 | auto关键字与for循环 | 指针空值
|
26天前
|
C++
C++ 20新特性之结构化绑定
在C++ 20出现之前,当我们需要访问一个结构体或类的多个成员时,通常使用.或->操作符。对于复杂的数据结构,这种访问方式往往会显得冗长,也难以理解。C++ 20中引入的结构化绑定允许我们直接从一个聚合类型(比如:tuple、struct、class等)中提取出多个成员,并为它们分别命名。这一特性大大简化了对复杂数据结构的访问方式,使代码更加清晰、易读。
32 0
|
2月前
|
编译器 C++ 计算机视觉
C++ 11新特性之完美转发
C++ 11新特性之完美转发
49 4
|
2月前
|
Java C# C++
C++ 11新特性之语法甜点1
C++ 11新特性之语法甜点1
31 4
|
2月前
|
安全 程序员 编译器
C++ 11新特性之auto和decltype
C++ 11新特性之auto和decltype
38 3
|
2月前
|
设计模式 缓存 安全
C++ 11新特性之week_ptr
C++ 11新特性之week_ptr
35 2