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之Lanbda表达式(匿名函数)

C++11之右值引用:移动语义和完美转发(带你了解移动构造函数、纯右值、将亡值、右值引用、std::move、forward等新概念)

C++11之委派构造函数

C++11之显式转换操作符-explicit

C++11之初始化列表

C++11之防止类型收窄(列表初始化)

C++11之用户自定义字面量(ClassType operator““_C(param…))



场景需求

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

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

林夕07

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值