c++ 什么时候应该使用精心设计的类型说明符?

vql8enpb  于 4个月前  发布在  其他
关注(0)|答案(5)|浏览(48)

是否有特别好的理由选择使用elaborated type specifier?例如,在某些情况下,需要使用templatetypename关键字来消除依赖template或类型的歧义。
我想不出任何例子,这将发生的东西,如枚举。采取以下代码示例:

enum Foo { A,  B };

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

字符串
为什么我可以选择使用baz()提供的语法而不是bar()(反之亦然)?有什么不明确的情况吗?

flvlnr44

flvlnr441#

没有理由使用这样的说明符,除非你正在处理的情况下,名称是隐藏的名称不同的“种类”。例如,它是完全法律的声明变量命名为Foo后枚举声明,因为,非正式地说,对象名和类型名生活在独立的“命名空间”(见3.3/4更正式的规范)

enum Foo { A, B };

int Foo;

字符串
int Foo声明之后,您的bar声明将变得无效,而更详细的baz声明将保持有效。

7fhtutme

7fhtutme2#

声明用户定义的类型需要精细的类型说明符。一个用例是正向声明类型。如果你有一个函数与你在作用域中可见的enum同名,那么你可能需要在函数声明中使用精细的类型说明符:

enum A { A_START = 0 };

void A(enum A a) {}

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

字符串

92vpleto

92vpleto3#

选择使用精心设计的类型说明符的一个很好的理由是在头中转发声明结构或类。

// a.h
struct foo {};

字符串
您可以包含该头文件以原型化您的函数

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


或者使用精心设计的类型:

void foo(struct foo * ) ;


或者,使用详细类型明确地向前声明:

struct foo ;
void foo( foo * ) ;


最后两种方法中的任何一种都可以帮助避免你的标题逐渐退化成一个完全连接的网络,其中包括任何一个标题都会拉入所有其余的内容(我在一个软件产品上工作,不幸的是,这是真的,迫使你在许多种变化之后重建世界,你可以想象这些变化在逻辑上是孤立的)。
据我所知,C11也将允许这种枚举的前向引用,这在C01编译器中是不允许的。

k4ymrczo

k4ymrczo4#

一个可能出现的例子是,当你有一个类型和一个同名的非类型元素时。通过使用精心设计的类型说明符,你可以显式地请求类型:

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

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

ckocjqey

ckocjqey5#

下面解释的前向声明是详细类型说明符的一种表现形式。
在一些面向对象的语言如C和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;
 };

相关问题