码农知识堂 - 1000bd
  •   Python
  •   PHP
  •   JS/TS
  •   JAVA
  •   C/C++
  •   C#
  •   GO
  •   Kotlin
  •   Swift
  • 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…))


    目录

    • 系列文章
    • 场景需求
    • 命名空间嵌套的弊端
    • 内联名字空间(inline namespace)
      • 通过内联命名空间优化嵌套问题
      • 内联命名空间配合宏使用
    • ADL特性(Argument-Dependent name Lookup)
      • 总结


    场景需求

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

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

    命名空间嵌套的弊端

    在下面这段代码中,用户1将他的代码封装为LINXI,然后内部又进行了细分为BB、CC、DD。而且在DD空间下使用了BB的类型。

    #include 
    
    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 
    
    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命名空间,这里我们使用到了__cplusplusC++的版本宏,如果当前的版本与宏的关系成立那么就将该命名空间内联到LINXI中。

    #include 
    
    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 
    
    using namespace std;
    
    namespace ADL
    {
    	struct A{};
    	void ADLFunction(A a);
    }
    
    int main()
    {
    	ADL::A a;
    
    	ADLFunction(a);
    
    	return 0;
    }
    

    可以看到我们没有写完参数时,编译器一直提醒我们添加ADL命名空间。
    在这里插入图片描述

    在写完之后,编译器已经可以识别这个函数了。并且可以正常运行。
    在这里插入图片描述

    总结

    ADL带来了一定的便捷性,但也破坏了命名空间的封装性,而且更多人觉得ADL特性缺大于优,比较鸡肋。所以我们还是在使用时还是通过::方式吧。

  • 相关阅读:
    树莓派学习笔记
    FFmpeg+javacpp+javacv使用
    Cholesterol-PEG-Maleimide CLS-PEG-MAL 胆固醇-聚乙二醇-马来酰亚胺概述
    某果的一个小参数分析
    MySQL——数据的删除以及MySQL中的约束
    自律挑战30天
    一篇打通线程 等待 && 中断
    C语言基于AVL树实现简单的文件数据库
    shell脚本 正则表达式
    ThreeJS光照类型详解及示例演示
  • 原文地址:https://blog.csdn.net/qq_45254369/article/details/127113224
  • 最新文章
  • 沪漂五周年了:我越来越迷茫了
    Agentic Skill Routing 实战:别再把所有 Skill 塞进 AI Agent 上下文
    MySQL-Seconds_behind_master的精度误差
    [MAF预定义ChatClient中间件-03]CachingChatClient——利用缓存省钱省时间
    AI的至暗历史:从万众期待到被政府撤资,AI的两次死亡徘徊
    Agent OS :五种驯服不确定性的范式
    PortSwigger SQL注入LAB11
    数据库即时编译JIT
    [Begin]AI Learn Data Day 0
    深度学习进阶(二十七)现代 LLM 的核心架构设计其二:SwiGLU
  • 热门文章
  • 十款代码表白小特效 一个比一个浪漫 赶紧收藏起来吧!!!
    奉劝各位学弟学妹们,该打造你的技术影响力了!
    五年了,我在 CSDN 的两个一百万。
    Java俄罗斯方块,老程序员花了一个周末,连接中学年代!
    面试官都震惊,你这网络基础可以啊!
    你真的会用百度吗?我不信 — 那些不为人知的搜索引擎语法
    心情不好的时候,用 Python 画棵樱花树送给自己吧
    通宵一晚做出来的一款类似CS的第一人称射击游戏Demo!原来做游戏也不是很难,连憨憨学妹都学会了!
    13 万字 C 语言从入门到精通保姆级教程2021 年版
    10行代码集2000张美女图,Python爬虫120例,再上征途
小工具 小游戏
Copyright © 2022 侵权请联系2656653265@qq.com    京ICP备2022015340号-1

京公网安备 11010502049817号