C++ typedef 的头文件最佳实践

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2356548/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 23:12:10  来源:igfitidea点击:

Header file best practices for typedefs

c++typedef

提问by Rob Napier

I'm using shared_ptr and STL extensively in a project, and this is leading to over-long, error-prone types like shared_ptr< vector< shared_ptr<const Foo> > >(I'm an ObjC programmer by preference, where long names are the norm, and still this is way too much.) It would be much clearer, I believe, to consistently call this FooListPtrand documenting the naming convention that "Ptr" means shared_ptr and "List" means vector of shared_ptr.

我在一个项目中广泛使用了 shared_ptr 和 STL,这导致了过长的、容易出错的类型,比如shared_ptr< vector< shared_ptr<const Foo> > >(我是一个偏好的 ObjC 程序员,长名称是常态,但这仍然太多了.) 我相信,一致地调用它FooListPtr并记录命名约定“Ptr”表示 shared_ptr 并且“List”表示 shared_ptr 的向量会更清楚。

This is easy to typedef, but it's causing headaches with the headers. I seem to have several options of where to define FooListPtr:

这很容易 typedef,但它会导致标题令人头疼。我似乎有几个定义在哪里的选项FooListPtr

  • Foo.h. That entwines all the headers and creates serious build problems, so it's a non-starter.
  • FooFwd.h ("forward header"). This is what Effective C++suggests, based on iosfwd.h. It's very consistent, but the overhead of maintaining twice the number of headers seems annoying at best.
  • Common.h (put all of them together into one file). This kills reusability by entwining a lot of unrelated types. You now can't just pick up one object and move it to another project. That's a non-starter.
  • Some kind of fancy #define magic that typedef's if it hasn't already been typedefed. I have an abiding dislike for the preprocessor because I think it makes it hard for new people to grok the code, but maybe....
  • Use a vector subclass rather than a typedef. This seems dangerous...
  • Foo.h。这会缠绕所有标题并造成严重的构建问题,因此它不是入门者。
  • FooFwd.h(“转发标题”)。这是Effective C++建议的,基于 iosfwd.h。这是非常一致的,但是维护两倍标题数量的开销充其量似乎很烦人。
  • Common.h(将它们全部放在一个文件中)。这通过缠绕许多不相关的类型来扼杀可重用性。您现在不能只是拿起一个对象并将其移动到另一个项目中。那是无从下手。
  • 如果它还没有被 typedef,那么 typedef 的某种花哨的 #define 魔法。我一直不喜欢预处理器,因为我认为新人很难理解代码,但也许......
  • 使用向量子类而不是 typedef。这似乎很危险……

Are there best practices here? How do they turn out in real code, when reusability, readability and consistency are paramount?

这里有最佳实践吗?当可重用性、可读性和一致性至关重要时,它们在实际代码中的表现如何?

I've marked this community wiki if others want to add additional options for discussion.

如果其他人想添加其他讨论选项,我已经标记了这个社区维基。

回答by AshleysBrain

I'm programming on a project which sounds like it uses the common.hmethod. It works very well for that project.

我正在对一个听起来像是使用该common.h方法的项目进行编程。它非常适合该项目。

There is a file called ForwardsDecl.hwhich is in the pre-compiled header and simply forward-declares all the important classes and necessary typedefs. In this case unique_ptris used instead of shared_ptr, but the usage should be similar. It looks like this:

ForwardsDecl.h预编译头文件中有一个名为的文件,它简单地向前声明了所有重要的类和必要的 typedef。在这种情况下unique_ptr使用 代替shared_ptr,但用法应该相似。它看起来像这样:

// Forward declarations
class ObjectA;
class ObjectB;
class ObjectC;

// List typedefs
typedef std::vector<std::unique_ptr<ObjectA>> ObjectAList;
typedef std::vector<std::unique_ptr<ObjectB>> ObjectBList;
typedef std::vector<std::unique_ptr<ObjectC>> ObjectCList;

This code is accepted by Visual C++ 2010 even though the classes are only forward-declared (the full class definitions are not necessary so there's no need to include each class' header file). I don't know if that's standard and other compilers will require the full class definition, but it's useful that it doesn't: another class (ObjectD) can have an ObjectAList as a member, without needing to include ObjectA.h - this can really help reduce header file dependencies!

此代码被 Visual C++ 2010 接受,即使这些类只是前向声明的(不需要完整的类定义,因此不需要包含每个类的头文件)。我不知道这是否是标准的,其他编译器是否需要完整的类定义,但它不需要:另一个类(ObjectD)可以将 ObjectAList 作为成员,而无需包含 ObjectA.h - 这可以真的有助于减少头文件的依赖性!

Maintenance is not particularly an issue, because the forwards declarations only need to be written once, and any subsequent changes only need to happen in the full declaration in the class' header file (and this will trigger fewer source files to be recompiled due to reduced dependencies).

维护不是特别大的问题,因为 forwards 声明只需要写一次,任何后续的更改只需要在类的头文件中的完整声明中发生(这将导致更少的源文件由于减少了重新编译依赖)。

Finally it appears this can be shared between projects (I haven't tried myself) because even if a project does not actually declare an ObjectA, it doesn't matter because it was only forwards declared and if you don't use it the compiler doesn't care. Therefore the file can contain the names of classes across all projects it's used in, and it doesn't matter if some are missing for a particular project. All that is required is the necessary full declaration header (e.g. ObjectA.h) is included in any source(.cpp) files that actually usethem.

最后看来这可以在项目之间共享(我自己没有尝试过),因为即使项目实际上没有声明 ObjectA,也没关系,因为它只是转发声明,如果你不使用它,编译器不在乎。因此,该文件可以包含它所使用的所有项目的类的名称,并且对于特定项目是否缺少某些类并不重要。所需要的只是将必要的完整声明头(例如ObjectA.h)包含在实际使用它们的任何源(.cpp) 文件中。

回答by Konrad Rudolph

I would go with a combined approach of forward headers and a kind of common.hheader that is specific to your project and just includes all the forward declaration headers and any other stuff that is common and lightweight.

我会采用前向标头和一种common.h特定于您的项目的标头的组合方法,并且只包含所有前向声明标头和任何其他常见和轻量级的东西。

You complain about the overhead of maintaining twice the number of headers but I don't think this should be too much of a problem: the forward headers usually only need to know a very limited number of types (one?), and sometimes not even the full type.

您抱怨维护两倍于标头数量的开销,但我不认为这应该是一个太大的问题:前向标头通常只需要知道非常有限数量的类型(一种?),有时甚至不需要完整的类型。

You could even try auto-generating the headers using a script (this is done e.g. in SeqAn) if there are reallythat many headers.

如果真的有很多标题,您甚至可以尝试使用脚本自动生成标题(例如在SeqAn 中完成)。

回答by peterchen

+1 for documenting the typedef conventions.

+1 用于记录 typedef 约定。

  • Foo.h- can you detail the problems you have with that?
  • FooFwd.h- I'd not use them generally, only on "obvious hotspots". (Yes, "hotspots" arehard to determine). It doesn't change the rules IMO because when you do introduce a fwd header, the associated typedefs from foo.h move there.
  • Common.h- cool for small projects, but doesn't scale, I do agree.
  • Some kind of fancy #define... PLEASE NO!...
  • Use a vector subclass- doesn't make it better. You might use containment, though.
  • Foo.h- 你能详细说明你遇到的问题吗?
  • FooFwd.h- 我一般不会使用它们,只在“明显的热点”上使用。(是的,“热点”很难确定)。它不会改变 IMO 的规则,因为当您引入 fwd 标头时,来自 foo.h 的相关 typedef 会移动到那里。
  • Common.h- 对于小型项目很酷,但不能扩展,我同意。
  • 某种花哨的#define...请不要!...
  • 使用向量子类- 不会让它变得更好。不过,您可能会使用遏制措施。

So here the prelimenary suggestions (revised from that other question..)

所以这里是初步建议(从另一个问题修改..)

  1. Standard type headers <boost/shared_ptr.hpp>, <vector>etc. can go into a precompiled header / shared include file for the project. This is not bad.(I personally still include them where needed, but that works in addition to putting them into the PCH.)

  2. If the container is an implementation detail, the typedefs go where the container is declared (e.g. private class members if the container is a private class member)

  3. Associated types (like FooListPtr) go to where Foo is declarated, ifthe associated type is the primary use of the type. That's almost always true for some types - e.g. shared_ptr.

  4. If Foogets a separate forward declaration header, and the associated type is ok with that, it moves to the FooFwd.h, too.

  5. If the type is only associated with a particular interface (e.g. parameter for a public method), it goes there.

  6. If the type is shared (and does not meet any of the previous criteria), it gets its own header. Note that this also means to pull in all dependencies.

  1. 标准型头<boost/shared_ptr.hpp><vector>等可以进入预编译的头/共享包括对项目文件。这还不错。(我个人仍然在需要的地方包含它们,但这除了将它们放入 PCH 之外还有效。)

  2. 如果容器是一个实现细节,则 typedef 会在容器被声明的地方(例如,如果容器是私有类成员,则为私有类成员)

  3. 如果关联类型是该类型的主要用途FooListPtr关联类型(如)会转到声明 Foo 的位置。对于某些类型,这几乎总是正确的 - 例如shared_ptr.

  4. 如果Foo获得单独的前向声明标头,并且关联类型可以,它也会移动到 FooFwd.h。

  5. 如果该类型仅与特定接口(例如公共方法的参数)相关联,则它会转到那里。

  6. 如果该类型是共享的(并且不满足上述任何条件),则它会获得自己的标头。请注意,这也意味着引入所有依赖项。

It feels "obvious" for me, but I agree it's not good as a coding standard.

对我来说感觉“显而易见”,但我同意它作为编码标准并不好。

回答by justin

I'm using shared_ptr and STL extensively in a project, and this is leading to over-long, error-prone types like shared_ptr< vector< shared_ptr > > (I'm an ObjC programmer by preference, where long names are the norm, and still this is way too much.) It would be much clearer, I believe, to consistently call this FooListPtr and documenting the naming convention that "Ptr" means shared_ptr and "List" means vector of shared_ptr.

我在一个项目中广泛使用了 shared_ptr 和 STL,这导致过长的、容易出错的类型,比如 shared_ptr< vector< shared_ptr > > (我是一个 ObjC 程序员偏好,其中长名称是常态,但这仍然太多了。)我相信,一致地调用这个 FooListPtr 并记录命名约定“Ptr”表示 shared_ptr 和“List”表示 shared_ptr 的向量会更清楚。

for starters, i recommend using good design structures for scoping (e.g., namespaces) as well as descriptive, non-abbreviated names for typedefs. FooListPtris terribly short, imo. nobody wants to guess what an abbreviation means (or be surprised to find Foo is const, shared, etc.), and nobody wants to alter their code simply because of scope collisions.

对于初学者,我建议使用良好的设计结构进行范围界定(例如,名称空间)以及 typedef 的描述性、非缩写名称。FooListPtr太短了,imo。没有人想猜测缩写的含义(或者惊讶地发现 Foo 是 const、shared 等),也没有人想仅仅因为范围冲突而改变他们的代码。

it may also help to choose a prefix for typedefs in your libraries (as well as other common categories).

它也可能有助于为您的库(以及其他常见类别)中的 typedef 选择前缀。

it's also a bad idea to drag types out of their declared scope:

将类型拖出其声明的范围也是一个坏主意:

namespace MON {
namespace Diddy {
class Foo;
} /* << Diddy */

/*...*/
typedef Diddy::Foo Diddy_Foo;

} /* << MON */

there are exceptions to this:

也有例外:

  • an entirely ecapsualted private type
  • a contained type within a new scope
  • 完全封装的私有类型
  • 新范围内的包含类型

while we're at it, usingin namespace scopes and namespace aliases should be avoided - qualify the scope if you want to minimize future maintentance.

当我们在做的时候,using应该避免使用命名空间范围和命名空间别名——如果你想最大限度地减少未来的维护,请限定范围。

This is easy to typedef, but it's causing headaches with the headers. I seem to have several options of where to define FooListPtr:

Foo.h. That entwines all the headers and creates serious build problems, so it's a non-starter.

这很容易 typedef,但它会导致标题令人头疼。我似乎有几种定义 FooListPtr 的选项:

Foo.h。这会缠绕所有标题并造成严重的构建问题,因此它不是入门者。

it may be an option for declarations which really depend on other declarations. implying that you need to divide packages, or there is a common, localized interface for subsystems.

对于真正依赖于其他声明的声明,它可能是一个选项。这意味着您需要划分包,或者子系统有一个通用的本地化接口。

FooFwd.h ("forward header"). This is what Effective C++ suggests, based on iosfwd.h. It's very consistent, but the overhead of maintaining twice the number of headers seems annoying at best.

FooFwd.h(“转发标题”)。这是 Effective C++ 所建议的,基于 iosfwd.h。这是非常一致的,但是维护两倍标题数量的开销充其量似乎很烦人。

don't worry about the maintenance of this, really. it is a good practice. the compiler uses forward declarations and typedefs with very little effort. it's not annoying because it helps reduce your dependencies, and helps ensure that they are all correct and visible. there really isn't more to maintain since the other files refer to the 'package types' header.

不要担心这个的维护,真的。这是一个很好的做法。编译器可以毫不费力地使用前向声明和 typedef。这并不烦人,因为它有助于减少您的依赖关系,并有助于确保它们都是正确和可见的。确实没有更多需要维护,因为其他文件引用了“包类型”标头。

Common.h (put all of them together into one file). This kills reusability by entwining a lot of unrelated types. You now can't just pick up one object and move it to another project. That's a non-starter.

Common.h(将它们全部放在一个文件中)。这通过缠绕许多不相关的类型来扼杀可重用性。您现在不能只是拿起一个对象并将其移动到另一个项目中。那是无从下手。

package based dependencies and inclusions are excellent (ideal, really) - do not rule this out. you'll obviously have to create package interfaces (or libraries) which are designed and structured well, and represent related classes of components. you're making an unnecessary issue out of object/component reuse. minimize the static data of a library, and let the link and strip phases do their jobs. again, keep your packages small and reusable and this will not be an issue (assuming your libraries/packages are well designed).

基于包的依赖项和包含非常好(理想,真的) - 不排除这一点。您显然必须创建设计和结构良好的包接口(或库),并表示组件的相关类。您正在因对象/组件重用而产生不必要的问题。最小化库的静态数据,让链接和剥离阶段完成它们的工作。再次,保持您的包小且可重用,这不会成为问题(假设您的库/包设计良好)。

Some kind of fancy #define magic that typedef's if it hasn't already been typedefed. I have an abiding dislike for the preprocessor because I think it makes it hard for new people to grok the code, but maybe....

如果它还没有被 typedef,那么 typedef 的某种花哨的 #define 魔法。我一直不喜欢预处理器,因为我认为新人很难理解代码,但也许......

actually, you may declare a typedef in the same scope multiple times (e.g., in two separate headers) - that is not an error.

实际上,您可以在同一范围内多次声明 typedef(例如,在两个单独的标头中)- 这不是错误。

declaring a typedef in the same scope with different underlying types isan error. obviously. you must avoid this, and fortunately the compiler enforces that.

在具有不同基础类型的同一范围内声明 typedef错误的。明显地。您必须避免这种情况,幸运的是编译器会强制执行此操作。

to avoid this, create a 'translation build' which includes the world - the compiler will flag declarations of typedeffed types which don't match.

为了避免这种情况,创建一个包含世界的“翻译构建”——编译器将标记不匹配的类型定义类型的声明。

trying to sneak by with minimal typedefs and/or forwards (which are close enough to free at compilation) is not worth the effort. sometimes you'll need a bunch of conditional support for forward declarations - once that is defined, it is easy (stl libraries are a good example of this -- in the event you are also forward declaring template<typename,typename>class vector;).

尝试使用最少的 typedef 和/或转发(在编译时足够接近以释放)是不值得的。有时,您需要对前向声明提供大量条件支持——一旦定义,就很容易(stl 库就是一个很好的例子——如果您也进行前向声明template<typename,typename>class vector;)。

it's best to just have all these declarations visible to catch any errors immediately, and you can avoid the preprocessor in this case as a bonus.

最好让所有这些声明都可见以立即捕获任何错误,并且在这种情况下您可以避免使用预处理器作为奖励。

Use a vector subclass rather than a typedef. This seems dangerous...

使用向量子类而不是 typedef。这似乎很危险……

a subclass of std::vectoris often flagged as a "beginner's mistake". this container was not meant to be subclassed. don't resort to bad practices simply to reduce your compile times/dependencies. if the dependency really is that significant, you should probably be using PIMPL, anyways:

的子类std::vector通常被标记为“初学者的错误”。这个容器不应该被子类化。不要仅仅为了减少编译时间/依赖性而诉诸不良做法。如果依赖关系真的那么重要,你可能应该使用 PIMPL,无论如何:

// <package>.types.hpp
namespace MON {
class FooListPtr;
}

// FooListPtr.hpp
namespace MON {
class FooListPtr {
    /* ... */
private:
    shared_ptr< vector< shared_ptr<const Foo> > > d_data;
};
}

Are there best practices here? How do they turn out in real code, when reusability, readability and consistency are paramount?

这里有最佳实践吗?当可重用性、可读性和一致性至关重要时,它们在实际代码中的表现如何?

ultimately, i've found a small concise package based approach the best for reuse, for reducing compile times, and minimizing dependence.

最终,我发现了一个基于小而简洁的包的方法,最适合重用、减少编译时间和最小化依赖性。

回答by Michael Daum

Unfortunately with typedefs you have to choose between not ideal options for your header files. There are special cases where option one (right in the class header) works well, but it sounds like it won't work for you. There are also cases where the last option works well, but it's usually where you are using the subclass to replace a pattern involving a class with a single member of type std::vector. For your situation, I'd use the forward declaring header solution. There's extra typing and overhead, but it wouldn't be C++ otherwise, right? It keeps things separate, clean and fast.

不幸的是,对于 typedef,您必须在不理想的头文件选项之间进行选择。在某些特殊情况下,选项一(就在类标题中)效果很好,但听起来它对您不起作用。也有最后一个选项工作良好的情况,但通常是您使用子类将涉及类的模式替换为类型为 std::vector 的单个成员。对于您的情况,我会使用前向声明标头解决方案。有额外的输入和开销,但否则就不会是 C++,对吗?它使事情分开,干净和快速。