C++仅标头库可避免"using namespace"污染

C++ header-only library avoid "using namespace" pollution

本文关键字:using namespace 污染 可避免 C++      更新时间:2023-10-16

我有一个只有头的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 中定义的

有没有办法让速记只能"私下"使用?

namespace library {
  namespace componentAImpl{
    using ::library::componentB;
    // ...
    inline namespace exports{
      struct foo{};
    }
  }
  namespace componentA{
    using namespace library::componentAImpl::exports;
  }
}

用户可以访问componentAImpl,但不应该访问。

同时,在library::componentA中暴露了一组干净的符号。

如果我正确理解你的问题,那么答案是否定的。如果在从componentA名称空间键入名称之前键入/读取componentA::太麻烦了(老实说,这是我的偏好(,那么你可以使用短名称空间别名:

namespace library { namespace componentB
{
// ...
namespace ca = componentA;
// ...
void foo(ca::SomeClass<T>);
// ...
} }

无论如何,我都不鼓励在头文件中使用指令:它们会导致名称冲突和维护问题。

好吧,C++11?使用声明将另一个命名空间的成员引入当前命名空间或块范围。

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);
       };
    }
}

经过一段时间的思考,我自己找到了解决方案。不幸的是,这并不简单。

//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::,但我再也帮不了她了。。