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特性缺大于优,比较鸡肋。所以我们还是在使用时还是通过::方式吧。


目录
相关文章
|
18天前
|
编译器 C++ 开发者
C++一分钟之-C++20新特性:模块化编程
【6月更文挑战第27天】C++20引入模块化编程,缓解`#include`带来的编译时间长和头文件管理难题。模块由接口(`.cppm`)和实现(`.cpp`)组成,使用`import`导入。常见问题包括兼容性、设计不当、暴露私有细节和编译器支持。避免这些问题需分阶段迁移、合理设计、明确接口和关注编译器更新。示例展示了模块定义和使用,提升代码组织和维护性。随着编译器支持加强,模块化将成为C++标准的关键特性。
48 3
|
24天前
|
编译器 C语言 C++
C++一分钟之-C++11新特性:初始化列表
【6月更文挑战第21天】C++11的初始化列表增强语言表现力,简化对象构造,特别是在处理容器和数组时。它允许直接初始化成员变量,提升代码清晰度和性能。使用时要注意无默认构造函数可能导致编译错误,成员初始化顺序应与声明顺序一致,且在重载构造函数时避免歧义。利用编译器警告能帮助避免陷阱。初始化列表是高效编程的关键,但需谨慎使用。
27 2
|
12天前
|
数据安全/隐私保护 C++
|
19天前
|
安全 JavaScript 前端开发
C++一分钟之-C++17特性:结构化绑定
【6月更文挑战第26天】C++17引入了结构化绑定,简化了从聚合类型如`std::tuple`、`std::array`和自定义结构体中解构数据。它允许直接将复合数据类型的元素绑定到单独变量,提高代码可读性。例如,可以从`std::tuple`中直接解构并绑定到变量,无需`std::get`。结构化绑定适用于处理`std::tuple`、`std::pair`,自定义结构体,甚至在范围for循环中解构容器元素。注意,绑定顺序必须与元素顺序匹配,考虑是否使用`const`和`&`,以及谨慎处理匿名类型。通过实例展示了如何解构嵌套结构体和元组,结构化绑定提升了代码的简洁性和效率。
35 5
|
20天前
|
存储 安全 编译器
【C++航海王:追寻罗杰的编程之路】引用、内联、auto关键字、基于范围的for、指针空值nullptr
【C++航海王:追寻罗杰的编程之路】引用、内联、auto关键字、基于范围的for、指针空值nullptr
37 5
|
23天前
|
C++
C++一分钟之—名空间(namespace)的作用与使用
【6月更文挑战第22天】C++的命名空间是代码组织的关键,防止命名冲突并促进模块化。通过`namespace`定义代码块,如`MyNamespace{...}`,并使用别名(`namespace MN = MyNamespace::...`)简化引用。避免过度使用和嵌套,以及`using namespace`的滥用,而应在小范围内导入所需成员。明智选择名空间名称并有效利用`using`声明,能提升代码可读性和专业性。
13 1
|
26天前
|
C++
C++对C的改进和拓展\名字空间
C++对C的改进和拓展\名字空间
8 1
|
28天前
|
C++
C++命名空间(namespace)的使用
C++命名空间(namespace)的使用
|
1月前
|
C++
c++中的using namespace std;
c++中的using namespace std;
|
12天前
|
存储 安全 编译器