【问题标题】:Is there a way to insert a global name into a namespace and make it available only from that namespace?有没有办法将全局名称插入命名空间并使其仅在该命名空间中可用?
【发布时间】:2016-09-20 20:57:58
【问题描述】:

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

extern "C" int fun(int);

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

namespace foo {
   using ::fun;
}

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

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

【问题讨论】:

  • 不,那不可能
  • 无回收。
  • 你可以尝试在命名空间中包含 c 标头。
  • a little magic 一切皆有可能(参见上一个链接 vs this)。

标签: c++ c++11 namespaces


【解决方案1】:

标准明确规定,外部 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 函数由于某些特定限制而在一个编译单元的全局命名空间中声明,您仍然可以组织在您将使用本地命名空间的其他编译单元中看不到此声明。根据上面的标准声明,这是完全有效的。但是,如果您在不同的编译单元中使用不同的可见性,则需要小心!

【讨论】:

    【解决方案2】:

    由于 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
    

    【讨论】:

    【解决方案3】:

    从 C++11 开始,您可以在 anonymous inline namespace 中使用不明确函数的 deleted 声明来执行此操作:

    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; }
                            ^~~
    

    Example.

    【讨论】:

    • 为什么需要匿名命名空间内联?
    • @conio 如果匿名命名空间不是内联的,那么::fun(0) 不会报错,尽管fun(0) 会报错。
    【解决方案4】:

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

    在您的标题中,声明包装器:

    namespace foo
    {
        int fun(int);
    }
    

    然后将其封装在一个私有源文件中:

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-06-04
      相关资源
      最近更新 更多