有没有办法将全局名称插入命名空间并使其仅从该命名空间中可用?

时间:2016-09-20 20:57:59

标签: c++ c++11 namespaces

假设我在全局范围内有一个"C"链接的函数。

extern "C" int fun(int);

然后,如果我想让它在名称空间中可见,我会这样做:

namespace foo {
   using ::fun;
}

但在此之后,除了::fun(0)之外,我仍然可以将其称为foo::fun(0)

所以我的问题是,有没有办法禁止函数fun的全局命名空间调用,只允许从namespace foo调用它?

4 个答案:

答案 0 :(得分:11)

该标准明确规定外部C函数在命名空间内声明,即使C不知道命名空间:

  

7.5 / 4:链接规范未建立范围。链接规范只应在命名空间范围内发生。

因此,您可以直接在foo namespace

中直接定义全局命名空间中的函数,而不是在全局命名空间中声明该函数。
// no declaration in global namespace, but... 
namespace foo {
    extern "C" int fun();
}

然后,您只能通过名称空间引用此功能:

foo::fun();  // yes !
::fun();     // doesn't compile !!!

请注意,您甚至可以在多个名称空间中声明外部C函数。它们都会引用相同的C函数:

namespace bar {
    extern "C" int fun();
}
...
foo::fun();  // ok
bar::fun();  // ok - same result as foo::fun(); 

这由标准保证:

  

7.5 / 6:最多一个具有特定名称的函数可以具有C语言链接。 C语言函数的两个声明   具有相同功能名称的链接(忽略名称空间名称)   限定它出现在不同的命名空间范围内引用   同样的功能。

请注意,如果由于某些特定约束而在一个编译单元的全局命名空间中声明了extern函数,您仍然可以组织在您将使用本地命名空间的其他编译单元中看不到此声明。根据上面的标准声明,这是完全有效的。但是,如果您在不同的编译单元中使用不同的可见性,则需要注意一些事项!

答案 1 :(得分:10)

由于C语言没有名称空间,extern "C"说明符的一部分表示源名称没有名称空间。

因此,以下代码将调用C函数foo

namespace bar
{
    extern "C" void foo();
}

void foo() {}  // note: legal, so no pre-existing ::foo()

void caller() {
    bar::foo();  // calls the C function
}

所以你可以做到

namespace bar
{
    extern "C"
    {
        #include "foo.h"
    }
}

或者如果您需要与C代码共享:

#ifdef __cplusplus
namespace bar
{
    extern "C"
    {
#endif
// .. C-compatible definitions etc
#ifdef __cplusplus
    }
}
#endif

答案 2 :(得分:9)

你可以在C ++ 11中使用deleted anonymous中的inline namespace模糊函数的Example声明来执行此操作:

inline namespace { int fun(int) = delete; }

尝试致电::fun(0)fun(0)会出错:

error: call of overloaded 'fun(int)' is ambiguous
note: candidate: int fun(int)
 extern "C" int fun(int);
                ^~~
note: candidate: int {anonymous}::fun(int) <deleted>
 inline namespace { int fun(int) = delete; }
                        ^~~

{{3}}

答案 3 :(得分:4)

您无法阻止任何明确的故意(误)使用,但您可以将其隐藏在单独的实施文件中,以使其难以意外使用:

在标题中,声明包装器:

namespace foo
{
    int fun(int);
}

然后在一个私有源文件中将其打包:

extern "C" int fun(int);

namespace foo
{
    int fun(int v) { return ::fun(v); }
}