在 C++ 头文件中放置 using 指令的位置

Where to put using directives in C++ header files

提问人:Hooloovoo 提问时间:9/2/2019 最后编辑:bolovHooloovoo 更新时间:9/2/2019 访问量:1948

问:

对于我的项目,我使用了一些非常复杂的数据结构,例如

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()
{
...
}

虽然这似乎有效,但我不确定这是否安全......我的直觉告诉我这有点丑陋。因此,在我像这样重写我的整个项目之前,我想我会问:有没有更好/更优雅/更安全的方法来做到这一点?还是我应该完全避免在头文件中使用类型别名?

C++ 作用域 using-directives 类型别名

评论

0赞 Bathsheba 9/2/2019
我从不在全球范围内这样做;仅在类或命名空间中。这样似乎污染更少。另外,由于我是老式的,我倾向于使用 .typedef
4赞 Some programmer dude 9/2/2019
您不需要在源文件中重新定义类型,只需记住 的作用域在类中即可。因此,您需要指定它,如FooTableBarBar::FooTable Bar::create_foo()
0赞 QCTDev 9/2/2019
@Someprogrammerdude 你应该从中做出一个答案。
3赞 Zoe is on strike 9/2/2019
如果你有一个命名空间,你几乎可以选择。就我个人而言,我会选择哪一个有意义(例如,如果 typedef 专门用于该类,我将其放在一个类中,如果它在其他地方使用而没有连接到特定类,则将其放在命名空间中)。此外,您不需要“重新陈述它”,您只需要改用 。Bar::FooTable
2赞 Gojita 9/2/2019
你还应该将你的函数定义为:auto Bar::create_foo() -> FooTable 或类似的东西 :-)

答:

11赞 Lightness Races in Orbit 9/2/2019 #1

但是,这有一个缺点,我需要在源文件中重述别名:

这是不正确的。你只需要让它然后指定正确的范围,所以你可以在范围之外调用它(包括返回类型,除非尾随!publicBar::FooTableBar

Bar::FooTable Bar::create_foo()
{ /* ... */ }

auto Bar::create_foo() -> FooTable
{ /* ... */ }

(只是 FooTable 在定义很好,因为它是一个成员!

你的方法很好,尽管我也会把所有东西都放在一个命名空间中。那么你的别名是否在类中并不重要:它仍然包含在你自己的代码中。这纯粹是一个风格问题,对其他任何人都几乎没有影响。

4赞 eerorika 9/2/2019 #2

但现在我读到以这种方式使用 using 可能会有问题,因为它会在包含此标头的所有内容上强制使用此别名。

此问题同样适用于您定义的类。包含此标头的所有程序都必须使用此定义。BarBar

所以,我只是把 using 放在类中

您减少了全局命名空间中的声明数。这很好。

但是,这有一个缺点,我需要在源文件中重述别名

这是不必要的。

如果将别名设为公共别名,则可以使用范围解析运算符将其引用为 。或者,您可以使用尾随返回类型,在该类型中,在类的作用域中查找上下文名称:Bar::FooTable

auto Bar::create_foo() -> FooTable

有一个更通用的解决方案:命名空间。将你自己的所有声明放入一个命名空间(可以进一步划分为子命名空间)。这样,您只需在全局命名空间中引入一个名称,从而大大降低了名称冲突的可能性。

在 C++ 头文件中放置 using 指令的位置

与您放置任何其他声明的推理相同。至少在你自己的命名空间中,但通常将声明放在尽可能窄的范围内是一个不错的经验法则。如果类型别名仅与该类一起使用,则成员类型别名很有意义。

0赞 Zoe is on strike 9/2/2019 #3

正如评论中已经提到的,您无需重新声明它。你只需要通过使用 if 在类中声明它来引用它。如果你在命名空间中声明它,这是一回事,但如果你在命名空间之外,你会使用命名空间名称。如果你使用 typedef,这也是一回事。Bar::FooTable

是在命名空间中还是在类中声明它完全取决于您。就我个人而言,我试图确保它有一个尽可能相关的范围。例如,如果我有一个仅用于特定类的 typedef,我会将 typedef 放在类中。如果它具有与特定类无关的全局值,则在命名空间中声明它。

话虽如此,我建议你不要在全局命名空间中声明它,以避免歧义,如果你由于某种原因发现自己有命名冲突,如果你最终在其他地方声明了一个不同的 typedef(或者我认为其他东西通常与你的 typedef/using 语句同名)。

此外,类中的 typedef 受访问修饰符的约束。默认情况下,它是私有的,这意味着您不能在类外使用它。如果您打算这样做,则需要将其公开。

在安全性方面,在全局范围内声明它并不是特别安全,特别是如果你把它与它结合起来(这可能是它本身的问题 - 见这个)。不过,您可以在自己的命名空间中声明它(),但将其声明为类会产生相同的效果。using namespacenamespace Baz { using FooTable = blah; /* more code*/}

请注意,命名空间和类本质上是作用域,它们有自己的动态。如果在源文件中编写代码,则可以访问在同一命名空间中声明的 typedef,而无需指定(在本例中为 )。它本质上公开了 typedef,类似于它在全局命名空间中的工作方式,但方式更受限制。更多相关内容请点击此处namespace BazBaz::FooTable