C ++-在名称中使用声明的范围

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

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

即 是在MyNamespace块中正确包含“使用boost :: numeric :: ublas :: vector”,还是会污染任何包含此标头的文件的名称空间?

Brett Ryland asked 2020-08-01T18:38:44Z
5个解决方案
39 votes

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

using指令会将您指定的名称当前可见的所有内容导入使用它的名称空间。 虽然您的using仅对MyNamespace的用户可见,但“外部”的其他内容对您的using声明可见。

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

根据经验,只有在.cpp文件中包含所有内容后,才应使用using声明。 Sutter和Alexandrescu撰写的“ C ++编码标准”一书中也有关于此确切问题的条目(条目59)。 这是一句名言:“但这是一个普遍的陷阱:许多人认为使用在命名空间级别(...)发出的声明是安全的。它们不是。它们至少同样危险,并且以更狡猾和更隐蔽的方式进行。”

即使您的名字using不太可能在其他任何地方都不存在(如此处所示),情况也可能变得很丑陋:在标头中,所有声明都应完全限定。 这很痛苦,但是否则,可能会发生奇怪的事情。

有关示例和问题的详细介绍,另请参见迁移到名称空间,使用声明和名称空间别名和名称空间命名。

ltjax answered 2020-08-01T18:39:18Z
12 votes

顾名思义,using声明是一个声明。 所有声明的作用域为封闭块(7.2),在本例中为名称空间MyNamespace。在该名称空间之外将不可见。

Björn Pollex answered 2020-08-01T18:39:39Z
4 votes

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

BЈовић answered 2020-08-01T18:39:59Z
2 votes

总而言之,出于两个原因,即使在名称空间中,标头中的using-声明也不行。 此外,非标头中的命名空间内的using声明容易出错或毫无意义(请参见结尾)。 标头中的using声明不正确,原因是:

  1. 它们将名称引入名称空间,这会影响所有包含标头的文件。
  2. 他们仅介绍已经看到的名称声明,这意味着行为取决于包含的顺序!

在您的示例中,这意味着:

  1. namespace foo { using ::bar; } 中,对于包含此标头的任何文件,vector现在可以解析为boost::numeric::ublas::vector:它“污染” MyNamespace命名空间。
  2. 导入哪个namespace foo { using ::bar; } 声明取决于此using-声明之前出现的声明,这取决于包含此标头的文件中include的顺序以及其所有include(正确地是预处理后翻译单元中声明的顺序) )。

根据您11年5月30日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:迁移到命名空间:“使用声明的命名空间永远不应出现在头文件中。”

通过在using-声明周围添加一个未命名的名称空间(以防止显示这些名称),然后在该未命名的名称空间之外添加另一个名称(以使所需名称本身可见),可以避免问题1的出现,但仍然存在问题 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声明本身中始终使用完全限定名称是最安全的(绝对值,从namespace foo { using ::bar; } 开始)。

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

  1. 函数(或结构或类或嵌套块)中的使用声明:可以。 这样可以最大程度地减小范围,这只是个问题:使用声明接近使用(易读性获胜),但是现在它们散布在整个文件中(易读性丢失)。
  2. 在(命名的)名称空间中使用具有相对名称的声明:容易出错。 这更加简洁,并增加了一些清晰度(它们所关联的名称空间中使用的相关名称),但可能是模棱两可的(就像包含相对路径的include),并且更安全地避免:

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

    namespace foo {
        using ::bar;
    }
    
  4. 在未命名的命名空间中使用-声明:毫无意义,有点危险。 例如,如果您在一个未命名的命名空间中有一个函数,说一个实现细节,那么您可以为其返回类型或参数类型使用using-声明。 这会将名称引入到该名称空间中(因此不能从其他文件中引用),但同样,您也不必在意,因为您不应该包含.cc / .cpp文件(未命名的名称空间特别是为了避免 名称在链接时发生冲突,此处不适用:这只是编译时别名)。 更糟糕的是,如果该名称已经存在,就会引入歧义!

Nils von Barth answered 2020-08-01T18:41:26Z
1 votes

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

Puppy answered 2020-08-01T18:41:46Z
translate from https://stackoverflow.com:/questions/6175705/scope-of-using-declaration-within-a-namespace