C++ 仅头文件库避免“使用命名空间”污染
Posted
技术标签:
【中文标题】C++ 仅头文件库避免“使用命名空间”污染【英文标题】:C++ header-only library avoid "using namespace" pollution 【发布时间】:2015-05-23 21:19:51 【问题描述】:我有一个带有多个命名空间的仅标头 C++ 库。
例如一个头文件可能包含
//header1.h
namespace library
namespace componentA
template<typename T>
class Someclass;
还有一个
//header2.h
namespace library
namespace componentB
template<typename T>
class SomeOtherClass
void Foo(const componentA::Someclass<T>& reference);
void Bar(const componentA::Someclass<T>& reference);
;
现在虽然这可行,但拥有一个仅包含头文件的库,一次又一次地编写命名空间变得乏味,尤其是当您涉及多个类和嵌套命名空间时。
所以我这样做了:
//new header2.h
namespace library
namespace componentB
using namespace componentA;
template<typename T>
class SomeOtherClass
void Foo(const Someclass<T>& reference);
void Bar(const Someclass<T>& reference);
void FooBar(const Someclass<T>& reference);
void FooWithBar(const Someclass<T>& reference);
;
虽然这当然更方便键入,但它的问题是,现在库的客户端也可以通过像这样使用 componentB
命名空间来使用 Someclass<T>
,这会导致接口模棱两可并最终导致代码不一致.
例如,客户现在可以使用
componentB::Someclass<T>
尽管它最初是在 componentA
中定义的
有没有办法让速记只能“私下”使用?
【问题讨论】:
【参考方案1】:namespace library
namespace componentAImpl
using ::library::componentB;
// ...
inline namespace exports
struct foo;
namespace componentA
using namespace library::componentAImpl::exports;
用户可以访问componentAImpl
,但不应该。
与此同时,library::componentA
中公开了一组干净的符号。
【讨论】:
不错。我认为这是正确的解决方案 /cc @mightyuhu @andy 我可能会使用不同的library
命名空间,并以 details_
开头的名称以避免诱惑代码完成者。而且我不确定 ADL 在某些情况下将如何工作。所以你必须部署它,看看它是否有粗糙的地方(我之前没有在我的代码中使用过上述模式)。【参考方案2】:
如果我正确理解您的问题,那么答案是否定的。如果在来自 componentA
命名空间的类型名称之前输入/读取 componentA::
是个大问题(老实说,这是我的偏好),那么您可以使用短命名空间别名:
namespace library namespace componentB
// ...
namespace ca = componentA;
// ...
void foo(ca::SomeClass<T>);
// ...
无论如何,我不鼓励在头文件中使用指令:它们会导致名称冲突和维护问题。
【讨论】:
是的,这样可以避免输入全名,但是客户端仍然可以访问 library::componentB::ca::Someclass嗯,C++ 11? Using-declaration 将另一个命名空间的成员引入当前命名空间或块作用域。
namespace library
namespace componentB
using componentA::SomeOtherClass;
template<typename T>
SomeOtherClass
void Foo(const Someclass<T>& reference);
void Bar(const Someclass<T>& reference);
void FooBar(const Someclass<T>& reference);
void FooWithBar(const Someclass<T>& reference);
;
【讨论】:
这不会改变客户端可以写componentB::SomeOtherClass
的事实,即使 SomeOtherClass
位于命名空间 componentA
中。
Andy Prowl,当然,但那将是另一个范围?
我不确定我明白你的意思
我会做一个额外的块范围来防止这种情况。
等等...额外的范围可能不是头库的解决方案。但无论如何对我来说使用声明更干净,但一些恶意用户当然可能会试图违反理智。【参考方案4】:
经过一段时间的思考,我自己想出了解决方案。不幸的是,这并不简单。
//header1.h
namespace lib_noexport
namespace library
namespace componentA
template<typename T>
class Someclass;
using namespace lib_noexport;
然后
//header2.h
namespace lib_noexport
using library::componentA::Someclass;
namespace library
namespace componentB
template<typename T>
class SomeOtherClass
void Foo(const Someclass<T>& reference)
;
using namespace lib_noexport;
现在这会产生以下结果:
library::componentB::SomeOtherClass<int> a; //works as expected
Someclass<int> b; //error
library::componentB::Someclass<int> c; //error
library::componentA::Someclass<int> b; //works
尽管如此,用户可能会愚蠢到使用未记录的 lib_noexport:: 但是我不能再帮助她了..
【讨论】:
您需要inner
命名空间吗?以上是关于C++ 仅头文件库避免“使用命名空间”污染的主要内容,如果未能解决你的问题,请参考以下文章