提问人:Hooloovoo 提问时间:9/2/2019 最后编辑:bolovHooloovoo 更新时间:9/2/2019 访问量:1948
在 C++ 头文件中放置 using 指令的位置
Where to put using directives in C++ header files
问:
对于我的项目,我使用了一些非常复杂的数据结构,例如
std::unordered_map<int, std::list<std::shared_ptr<const Foo>>>
为此,我想声明类型别名以提高可读性。我构建项目的代码已经通过将语句全局放在头文件中来做到这一点:using
// bar.h
#ifndef BAR_H
#define BAR_H
#include <unordered_map>
#include <list>
#include <memory>
#include "foo.h"
using FooTable = std::unordered_map<int, std::list<std::shared_ptr<const Foo>>>;
class Bar {
FooTable create_foo();
};
#endif
由于我的C++知识有点生疏,我只是采用了这种风格 - 但现在我读到以这种方式使用可能会有问题,因为它在包含此标头的所有内容上强制使用此别名。using
尽管在谷歌上搜索了很多,但我找不到关于如何正确处理这个问题的具体答案,只有很多关于不该做什么的陈述。所以,我只是把 using 放在类中:
// bar.h
#ifndef BAR_H
#define BAR_H
#include <unordered_map>
#include <list>
#include <memory>
#include "foo.h"
class Bar {
using FooTable = std::unordered_map<int, std::list<std::shared_ptr<const Foo>>>;
FooTable create_foo();
};
#endif
但是,这有一个缺点,我需要在源文件中重述别名:
// bar.cpp
#include "bar.h"
using FooTable = std::unordered_map<int, std::list<std::shared_ptr<const Foo>>>;
FooTable Bar::create_foo()
{
...
}
虽然这似乎有效,但我不确定这是否安全......我的直觉告诉我这有点丑陋。因此,在我像这样重写我的整个项目之前,我想我会问:有没有更好/更优雅/更安全的方法来做到这一点?还是我应该完全避免在头文件中使用类型别名?
答:
但是,这有一个缺点,我需要在源文件中重述别名:
这是不正确的。你只需要让它然后指定正确的范围,所以你可以在范围之外调用它(包括返回类型,除非尾随!public
Bar::FooTable
Bar
Bar::FooTable Bar::create_foo()
{ /* ... */ }
或
auto Bar::create_foo() -> FooTable
{ /* ... */ }
(只是 FooTable
在定义中很好,因为它是一个成员!
你的方法很好,尽管我也会把所有东西都放在一个命名空间中。那么你的别名是否在类中并不重要:它仍然包含在你自己的代码中。这纯粹是一个风格问题,对其他任何人都几乎没有影响。
但现在我读到以这种方式使用 using 可能会有问题,因为它会在包含此标头的所有内容上强制使用此别名。
此问题同样适用于您定义的类。包含此标头的所有程序都必须使用此定义。Bar
Bar
所以,我只是把 using 放在类中
您减少了全局命名空间中的声明数。这很好。
但是,这有一个缺点,我需要在源文件中重述别名
这是不必要的。
如果将别名设为公共别名,则可以使用范围解析运算符将其引用为 。或者,您可以使用尾随返回类型,在该类型中,在类的作用域中查找上下文名称:Bar::FooTable
auto Bar::create_foo() -> FooTable
有一个更通用的解决方案:命名空间。将你自己的所有声明放入一个命名空间(可以进一步划分为子命名空间)。这样,您只需在全局命名空间中引入一个名称,从而大大降低了名称冲突的可能性。
在 C++ 头文件中放置 using 指令的位置
与您放置任何其他声明的推理相同。至少在你自己的命名空间中,但通常将声明放在尽可能窄的范围内是一个不错的经验法则。如果类型别名仅与该类一起使用,则成员类型别名很有意义。
正如评论中已经提到的,您无需重新声明它。你只需要通过使用 if 在类中声明它来引用它。如果你在命名空间中声明它,这是一回事,但如果你在命名空间之外,你会使用命名空间名称。如果你使用 typedef,这也是一回事。Bar::FooTable
是在命名空间中还是在类中声明它完全取决于您。就我个人而言,我试图确保它有一个尽可能相关的范围。例如,如果我有一个仅用于特定类的 typedef,我会将 typedef 放在类中。如果它具有与特定类无关的全局值,则在命名空间中声明它。
话虽如此,我建议你不要在全局命名空间中声明它,以避免歧义,如果你由于某种原因发现自己有命名冲突,如果你最终在其他地方声明了一个不同的 typedef(或者我认为其他东西通常与你的 typedef/using 语句同名)。
此外,类中的 typedef 受访问修饰符的约束。默认情况下,它是私有的,这意味着您不能在类外使用它。如果您打算这样做,则需要将其公开。
在安全性方面,在全局范围内声明它并不是特别安全,特别是如果你把它与它结合起来(这可能是它本身的问题 - 见这个)。不过,您可以在自己的命名空间中声明它(),但将其声明为类会产生相同的效果。using namespace
namespace Baz { using FooTable = blah; /* more code*/}
请注意,命名空间和类本质上是作用域,它们有自己的动态。如果在源文件中编写代码,则可以访问在同一命名空间中声明的 typedef,而无需指定(在本例中为 )。它本质上公开了 typedef,类似于它在全局命名空间中的工作方式,但方式更受限制。更多相关内容请点击此处。namespace Baz
Baz::FooTable
评论
typedef
FooTable
Bar
Bar::FooTable Bar::create_foo()
Bar::FooTable