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

Where to put using directives in C++ header files

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

std::unordered_map<int, std::list<std::shared_ptr<const Foo>>>

我想为其声明类型别名以提高可读性。我构建项目的代码已经通过在 header 文件中全局放置 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 可能会有问题,因为它会在包含此 [ 的所有内容上强制使用此别名=32=].

尽管进行了大量的谷歌搜索,但我找不到关于如何正确处理这个问题的具体答案,只有很多关于不该做什么的陈述。所以,我只是把 using 放在 class:

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

虽然这似乎有效,但我不确定这是否安全...我的直觉告诉我它有点丑。所以在我像这样重写我的整个项目之前,我想我会问:是否有 better/more elegant/safer 方法来做到这一点?还是我应该完全避免在 header 文件中使用类型别名?

However this has the drawback, that I need to restate the alias in the source file:

这是不正确的。您只需要将其设置为 public 然后指定适当的范围,因此您可以在 Bar 的范围之外调用它 Bar::FooTable (其中包括 return 类型,除非尾随! ):

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

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

(只要 FooTable 就可以 定义中,因为它是成员!)

您的方法很好,尽管我也将所有内容都放在名称空间中。那么您的别名是否在 class 中并不重要:它仍然独立于您自己的代码中。这纯粹是一种风格问题,对其他人几乎没有影响。

but now I read that using using in that way can be problematic, since it forces this alias on everything that includes this header.

这个问题同样适用于您定义的 class Bar。所有包含此 header 的程序都必须使用此 Bar 的定义。

So, I just put the using inside the class

您减少了全局命名空间中的声明数量。这个不错。

However this has the drawback, that I need to restate the alias in the source file

这是不必要的。

如果您创建别名 public,您可以使用范围解析运算符将其引用为 Bar::FooTable。或者您可以使用尾随 return 类型,其中在 class:

范围内查找上下文名称
auto Bar::create_foo() -> FooTable

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

Where to put using directives in C++ header files

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

正如评论中已经提到的,您不需要重新声明它。如果您在 class 中声明它,您只需要使用 Bar::FooTable 来引用它。如果您在命名空间中声明它也是一样的,除非您在命名空间之外使用命名空间名称。如果你使用 typedef 也是一样的。

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

也就是说,我建议您不要在全局命名空间中声明它以避免歧义,如果您出于某种原因发现自己遇到命名冲突,如果您最终使用不同的 typedef(或者我认为general 与您的 typedef/using 语句同名)在其他地方声明。

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

就安全性而言,在全局范围内声明它并不是特别安全,尤其是当您将它与 using namespace 结合使用时(这可能是它自己的问题 - 请参阅 this)。您可以在自己的命名空间中声明它 (namespace Baz { using FooTable = blah; /* more code*/}),但将其声明为 class 会产生相同的效果。

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