在命名空间内使用声明的范围

时间:2011-05-30 11:30:19

标签: c++ namespaces scope using

在C ++头文件中是否安全(和正确)以在命名空间中使用using声明,如下所示:

#include <boost/numeric/ublas/vector.hpp>
namespace MyNamespace {
    using boost::numeric::ublas::vector;
    vector MyFunc(vector in);
}

即。 &#34;使用boost :: numeric :: ublas :: vector&#34;正确包含在MyNamespace块中,还是会污染包含此标头的任何文件的命名空间?

5 个答案:

答案 0 :(得分:36)

不,它不安全 - 它不会污染另一个命名空间,但由于其他原因它很危险:

using指令会将您指定的任何当前可见导入到您使用它的命名空间中。虽然只有[{1}}的用户可以看到您的using,但MyNamespace声明中会显示“外部”的其他内容。

那么在标题中使用时这有多危险?因为它将导入在声明点处可见的内容,所以确切的行为将取决于您在声明之前包含的标题的顺序(using可能有不同的内容)。因为你无法真正控制在标题之前包含哪些标题(你也不应该!标题应该是自给自足的!),这可能导致非常奇怪的问题,你的函数会在一个编译单元中找到一个东西,而另一个在下。

根据经验,{。}}声明只应在.cpp文件中全部包含后使用。在Sutter和Alexandrescu的“C ++编码标准”一书中,这个问题还有一个问题(项目59)。这里有一句话:“但这里是常见的陷阱:许多人认为使用在命名空间级别发布的声明(......)是安全的。它们不是。它们至少是危险的,并且以更微妙和更阴险的方式。”

即使名称boost::numeric::ublas::vector不太可能在其他任何地方都不存在(这可能是这里的情况),事情也会变得丑陋:在标题中,所有声明都应完全< / strong>合格。这很痛苦,但除此之外,可能会发生奇怪的事情。

另请参阅Migrating to NamespacesUsing-declarations and namespace aliasesNamespace Naming以获取示例和深入介绍的问题。

答案 1 :(得分:12)

正如名称所示,使用声明是一种声明。所有声明都限定在封闭块(7.2),在本例中为命名空间MyNamespace。它不会在该命名空间外可见。

答案 2 :(得分:4)

这是安全的,但它会污染MyNamespace名称空间。因此,包含该标头的任何文件都将在MyNamespace中包含函数/类。

答案 3 :(得分:1)

它不会污染任何其他名称空间,但它肯定会污染MyNamespace名称空间。

答案 4 :(得分:1)

总结一下,没有,标题中的using声明不正常,即使在命名空间内,也有两个原因。此外,非标头中命名空间内的using声明容易出错或无意义(参见结束)。标头中的using声明不正常,因为:

  1. 他们在命名空间中引入了一个名称,该名称会影响包含标题的所有文件
  2. 他们只介绍已经看过的名称的声明,这意味着的行为取决于包含的顺序!
  3. 在您的示例中,这意味着:

    1. MyNamespace内,vector现在可以解析为boost::numeric::ublas::vector,对于包含此标头的任何文件:它“污染”MyNamespace命名空间。
    2. 导入了哪些boost::numeric::ublas::vector声明取决于声明之前出现的声明,这取决于包含此标题的文件中包含的顺序,以及所有声明 包括(正确地说,在预处理之后,翻译单元中的声明顺序)。
    3. your comment of May 30 '11 at 11:51你实际上想要行为1,但由于问题2,这不起作用。你可以通过在所有其他人之后包含一个单独的标题来获得所需的行为(并完全限定名称在其他标题中)。但是,这很脆弱,因此不鼓励,最好只在转换到命名空间时保留:

      //--- file myheader.hpp ---
      #include <boost/numeric/ublas/vector.hpp>
      namespace MyNamespace {
          ::boost::numeric::ublas::vector MyFunc(::boost::numeric::ublas::vector in);
      }
      
      //--- file myproject_last.hpp ---
      namespace MyNamespace {
          using ::boost::numeric::ublas::vector;
      }
      
      //--- file myproject.cpp ---
      #include "myheader.hpp"
      // ...other includes
      #include "myproject_last.hpp"
      

      有关详细信息,此变通方法和建议,请参阅GotW #53: Migrating to Namespaces:“使用声明的命名空间绝不应出现在头文件中。”

      通过在using-declaration周围添加一个未命名的命名空间(以防止这些名称可见),然后在未命名的命名空间外添加另一个命名空间(以使所需名称​​本身可见),但仍然遇到问题2并丑化标题:

      //--- file myheader.hpp ---
      #include <boost/numeric/ublas/vector.hpp>
      namespace MyNamespace {
          namespace {
              using ::boost::numeric::ublas::vector;
              vector MyFunc(vector in);
          }
          using MyFunc; // MyNamespace::(unique)::MyFunc > MyNamespace::MyFunc
      }
      

      由于这些问题,您应该只在非标头(.cc / .cpp)文件中使用using声明:这不会影响其他文件,因此避免了问题1;并且包含了所有标题,因此避免了问题2。在这种情况下,无论是否将它们放在命名空间中都是一种品味问题,因为它们不会影响其他文件;在using-declaration本身中始终使用完全限定名称是最安全的(绝对值,从::开始)。

      最简单的方法是将所有using声明放在文件的顶部,包含之后,但在任何命名空间之外:这是安全,明确,易于阅读,并允许在整个文件中使用这些名称。一些常见的偏差:

      1. 在函数(或结构或类或嵌套块)中使用声明:精细。这最小化了范围,只是一个品味问题:使用声明接近使用(易读性赢),但现在它们分散在整个文件中(易读性丢失)。
      2. 在(命名)命名空间中使用带有相对名称的声明:容易出错。这更简洁,并增加了一些清晰度(与它们相关的命名空间中使用的相关名称),但可能含糊不清(就像包含相对路径一样),并且更安全避免:

        using ::foo::bar;
        namespace foo { ... }
        
        namespace foo {
            // Implicitly ::foo:bar, could be ::bar, or ::other::foo::bar.
            using bar;
        }
        
      3. 在命名命名空间中使用带有绝对名称的声明:无意义。这仅将名称引入命名空间,但您不应该关心,因为您不应该包含.cc / .cpp文件:

        namespace foo {
            using ::bar;
        }
        
      4. 在未命名的命名空间中使用声明:毫无意义,稍微危险。例如,如果您在未命名的命名空间中有一个函数,比如一个实现细节,那么您可以为其返回类型或参数类型设置一个using声明。这会将名称引入该命名空间(因此无法从其他文件中引用),但您不应该关心,因为您不应该包含.cc / .cpp文件(未命名的命名空间尤其适用于避免在链接时命名冲突,这在这里不适用:它只是一个编译时别名)。更糟糕的是,如果该名称已存在,则会引入歧义!