何时使用精心设计的类型说明符

时间:2012-06-05 18:07:36

标签: c++ coding-style

是否有特别好的理由选择使用精心设计的类型说明符?例如,在某些情况下,需要使用templatetypename关键字来消除依赖template或类型的歧义。

但我想不出任何例子,如枚举这样的事情。请使用以下代码示例:

enum Foo { A,  B };

void bar(Foo foo);
void baz(enum Foo foo);

为什么我可以选择使用baz()提供的bar()语法(反之亦然)?有什么含糊不清的案子吗?

5 个答案:

答案 0 :(得分:7)

没有理由使用这样的说明符,除非您正在处理名称被不同“种类”的名称隐藏的情况。例如,在枚举声明之后声明一个名为Foo的变量是完全合法的,因为,非正式地说,对象名称和类型名称存在于独立的“命名空间”中(更多正式规范参见3.3 / 4)

enum Foo { A, B };

int Foo;

int Foo声明后,您的bar声明将失效,而更精细的baz声明仍然有效。

答案 1 :(得分:3)

声明用户定义的类型需要详细说明的类型说明符。一个用例是转发声明您的类型。万一你有一个与enum同名的函数,你在范围内可见,你可能需要在函数声明中使用详细的类型说明符:

enum A { A_START = 0 };

void A(enum A a) {}

int main() {
   enum A a;
   A( a );
}

答案 2 :(得分:0)

可能出现的一个示例是当您拥有类型和同名的非类型元素时。通过使用详细类型说明符,您可以显式请求类型:

struct foo {};
void foo(struct foo) {}
int main() {
   struct foo f;
   foo(f);
}

如果没有精心设计的类型说明符,foo中的main会引用void foo(struct foo),而不是类型struct foo。现在,我不希望它出现在生产代码中,但您只是问了一个重要的例子。如果类型和函数(或变量)在不同的名称空间中定义,其中先前通过查找找到非类型,则会发生同样的情况。您可以将上面的struct替换为enum

答案 3 :(得分:0)

选择使用详细类型说明符的一个很好的理由是在头文件中转发声明结构或类。给定一个定义类型的标题

// a.h
struct foo {};

您可以将该标题包含在原型功能

#include "a.h"
void foo(foo * ) ;

或使用精心制作的类型:

void foo(struct foo * ) ;

或者,使用精心设计的类型展示声明:

struct foo ;
void foo( foo * ) ;

最后两种方法中的任何一种都可以帮助避免标题逐渐退化为完全连接的网络,其中包括任何单个标题拉入其余所有内容(我在软件产品上工作,这很遗憾,迫使您重建经过多次变化之后,你可以想象的世界会在逻辑上被孤立出来。)

据我所知,C ++ 11还允许对枚举进行这种前向引用,这在C ++ 01编译器中目前是不允许的。

答案 4 :(得分:0)

下面解释的前向声明是详细类型说明符的一种表现形式。

<块引用>

在一些面向对象的语言中,如 C++ 和 Objective-C,它是 有时需要提前声明类。这是在 需要知道类名的情况 一种类型,但不需要知道结构。

在 C++ 中,类和结构可以像这样前向声明:

class MyClass; 

struct MyStruct; 

在 C++ 中,类可以是 如果您只需要使用指向该类的指针,则前向声明 类型(因为所有对象指针的大小都相同,这就是 编译器关心)。这在课堂上特别有用 定义,例如如果一个类包含一个指针成员(或 引用)到另一个类。

前向声明用于避免不必要的耦合,这有助于 通过减少头文件包含的数量来减少编译时间。 这具有三重优势:

  • 减少 #include 打开的文件数(因此 操作系统调用)
  • 减少预处理文件的体积 (因为不包括标题)
  • 减少重新编译的影响,当 前向声明的类被修改。

类的前向声明是 如果您需要使用实际的类类型,则不够,例如, 如果你有一个成员的类型直接是那个类(不是一个 指针),或者如果您需要将其用作基类,或者如果您需要 在方法中使用类的方法。

这允许您使用指向类型的指针,而不必在头文件中包含相关的头文件(相反,您只需要在 .cpp 文件中使用它)。

这有助于减少依赖关系,从而缩短编译时间。

并不总是需要它,特别是对于 Unreal Engine 核心类,因为这些通常无论如何都包含在内。但是,如果您没有明确包含头文件,最好使用前缀。如果您这样做,如果包含头文件的任何文件更改为不包含它,您的代码仍将编译。

虽然提前声明很好,但您应该避免 ETS。如果您想转发声明您的类型,最好在文件范围内明确声明:

不好:

class Bad
 {
     void Func(class Y* YParam);
     class Z* ZProp;
 };
 

好:

 class Y;
 class Z;
 class Good
 {
     void Func(Y* YParam);
     Z* ZProp;
 };