提问人:Brett Ryland 提问时间:5/30/2011 最后编辑:EnlicoBrett Ryland 更新时间:9/15/2021 访问量:31058
在命名空间中使用声明的范围
scope of using declaration within a namespace
问:
在 C++ 头文件中使用命名空间中的 using 声明是否安全(且正确),如下所示:
#include <boost/numeric/ublas/vector.hpp>
namespace MyNamespace {
using boost::numeric::ublas::vector;
vector MyFunc(vector in);
}
即“using boost::numeric::ublas::vector”是否正确包含在 MyNamespace 块中,或者这会污染包含此标头的任何文件的命名空间?
答:
它是安全的,但它会污染 MyNamespace 命名空间。因此,任何包含该标头的文件都将在 MyNamespace 中具有函数/类。
评论
顾名思义,using 声明就是声明。所有声明的范围都限定为封闭块 (7.2),在本例中为 namespace 。它将在该命名空间之外不可见。MyNamespace
评论
boost::numeric::ublas::vector
using namespace MyNamespace;
std::vector
using namespace std;
它不会污染任何其他命名空间,但肯定会污染 MyNamespace 命名空间。
不,它不安全 - 它不会污染另一个命名空间,但由于其他原因,它是危险的:
指令会将您指定的名称当前可见的任何内容导入到使用它的命名空间中。虽然 your 将仅对 的用户可见,但来自“外部”的其他内容将对您的声明可见。using
using
MyNamespace
using
那么,当在标题中使用时,这有什么危险呢?因为它将导入在声明点可见的内容,所以确切的行为将取决于您在声明之前包含的标头的顺序(可能有不同的内容可见)。由于您无法真正控制在标头之前包含哪些标头(也不应该!标头应该是自给自足的!),这可能会导致非常奇怪的问题,即您的函数将在一个编译单元中找到一件事,而在下一个编译单元中找到另一件事。boost::numeric::ublas::vector
根据经验,只有在 .cpp 文件中包含所有内容后,才应使用声明。在Sutter和Alexandrescu合著的《C++编码标准》(Risk 59)一书中,也有关于这个问题的文章。这是一句话:using
但这里有一个常见的陷阱:许多人认为使用在命名空间级别 (...) 发出的声明是安全的。他们不是。它们至少同样危险,而且是以一种更微妙、更阴险的方式。
即使你的名字不太可能在其他任何地方都不存在(就像这里的情况一样),事情也会变得丑陋:在标头中,所有声明都应该是完全限定的。这是痛苦的,但除此之外,奇怪的事情可能会发生。using
另请参阅迁移到命名空间、使用声明和命名空间别名和命名空间命名,了解示例和深入描述的问题。
评论
using boost::numeric::ublas::vector
using namespace boost::numeric::ublas
boost::numeric::ublas
namespace MyNamespace { }
using namespace std; using namespace MyNamespace;
MyNamespace
vector
using ::boost::numeric::ublas::vector
using namespace std; using namespace MyNamespace
using namespace std; using namespace boost::numeric::ublas;
std::
using ::std::vector
using namespace
using
总而言之,不,即使在命名空间中,在标头中使用声明也是不行的,原因有 2 个。此外,在非标头的命名空间中使用声明容易出错或毫无意义(见结尾)。在标头中使用声明是不行的,因为:
- 它们在命名空间中引入了一个名称,这会影响包含标头的所有文件。
- 它们只引入已经看到的名称的声明,这意味着行为取决于包含的顺序!
在您的示例中,这意味着:
- 对于包含此标头的任何文件,现在 中 可能会解析为 :它会“污染”命名空间。
MyNamespace
vector
boost::numeric::ublas::vector
MyNamespace
- 导入哪些声明取决于在此 using-declaration 之前显示的声明,这取决于包含此标头的文件中 includes 的顺序,以及它的所有 include(正确地说,是预处理后转换单元中声明的顺序)。
boost::numeric::ublas::vector
根据您在 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-declaration 周围添加一个未命名的命名空间(以防止这些名称可见)然后在未命名的命名空间之外添加另一个命名空间(使所需名称本身可见)来避免问题 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-declarations:这不会影响其他文件,因此可以避免问题 1;并且所有标头都已包含,因此避免了问题 2。在这种情况下,是否将它们放在命名空间中是一个品味问题,因为它们不会影响其他文件;最安全的做法是在 using 声明本身中始终使用完全限定的名称(绝对,以 开头)。::
最简单的方法是将所有 using 声明放在文件顶部,在 includes 之后,但在任何命名空间之外:这是安全的、明确的、易于阅读的,并且允许在整个文件中使用名称。一些常见的偏差:
- 在函数(或结构、类或嵌套块)中使用声明:很好。这最小化了范围,只是一个品味问题:using-declaration 接近使用(易读性获胜),但它们现在分散在整个文件中(易读性损失)。
在(命名的)命名空间中具有相对名称的 Using-declaration:错误倾向。这更简洁,并增加了一些清晰度(在它们相关的命名空间中使用的相关名称),但可能不明确(就像相对路径的包含一样),并且避免使用会更安全:
using ::foo::bar; namespace foo { ... } namespace foo { // Implicitly ::foo:bar, could be ::bar, or ::other::foo::bar. using bar; }
在命名命名空间中使用绝对名称的声明:无意义。这只会将名称引入命名空间,但您不应该关心,因为您不应该包含 .cc/.cpp 文件:
namespace foo { using ::bar; }
在未命名的命名空间中使用声明:毫无意义,有点危险。例如,如果你在一个未命名的命名空间中有一个函数,比如一个实现细节,那么你可以有一个 using 声明来表示它的返回类型或参数类型。这会将名称引入该命名空间(因此无法从其他文件引用),但同样,您不应该关心,因为您不应该包含 .cc/.cpp 文件(未命名的命名空间特别用于避免链接时的名称冲突,这在这里不适用:它只是一个编译时别名)。更糟糕的是,如果该名称已经存在,则会产生歧义!
评论
MyNamespace
typedef
boost::numeric::ublas::vector