【问题标题】:When to use an elaborated type specifier何时使用详细类型说明符
【发布时间】:2012-06-05 18:07:35
【问题描述】:

选择使用详细类型说明符有什么特别好的理由吗?例如,在某些情况下,需要使用templatetypename 关键字来消除依赖template 或类型的歧义。

但我想不出任何例子会发生这种情况,例如枚举。以如下代码为例:

enum Foo { A,  B };

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

为什么我可以选择使用baz() 提供的语法而不是bar()(反之亦然)?有什么模棱两可的情况吗?

【问题讨论】:

  • baz 语法不只是旧 C 风格的遗留物,你没有对枚举进行类型定义,所以你必须明确地将其称为枚举?就像你如何使用 struct Foo f;如果您没有对结构进行类型定义?在 C++ 中,typedef 隐含在 enum/struct 声明中,因此您不必声明类型是 enum/struct 可能吗?
  • 在标准中,前向声明使用详细的类型说明符。
  • templatetypename 不是详细类型说明符的一部分。

标签: c++ coding-style


【解决方案1】:

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

enum Foo { A, B };

int Foo;

int Foo 声明之后,您的bar 声明将失效,而更详细的baz 声明将保持有效。

【讨论】:

  • 有道理;我没有考虑过这个案子。当然,我会打任何这样做的人,但我确实想知道这些极端情况。谢谢! (还有其他人。)
【解决方案2】:

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

enum A { A_START = 0 };

void A(enum A a) {}

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

【讨论】:

    【解决方案3】:

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

    // a.h
    struct foo {};
    

    您可以包含该标头来对您的函数进行原型设计

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

    或使用详细类型:

    void foo(struct foo * ) ;
    

    或者,使用详细类型明确转发声明:

    struct foo ;
    void foo( foo * ) ;
    

    最后两种方法中的任何一种都可以帮助避免您的标头逐渐退化为一个完全连接的网络,其中包括任何单个标头都会拉入所有其余部分(我正在开发一个软件产品,可悲的是,这会迫使您重新构建经过你可以想象的许多变化之后的世界在逻辑上是孤立的)。

    我知道 C++11 也将​​允许对枚举进行这种前向引用,这是 C++01 编译器目前不允许的。

    【讨论】:

      【解决方案4】:

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

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

      如果没有详细说明的类型说明符,main 中的foo 指的是void foo(struct foo),而不是类型struct foo。现在,我不希望它出现在生产代码中,但您只要求提供一个重要的示例。如果类型和函数(或变量)被定义在不同的命名空间中,在这些命名空间中通过查找更早地找到了非类型,也会发生同样的情况。您可以将struct 替换为上面的enum

      【讨论】:

        【解决方案5】:

        下面解释的前向声明是Elaborated Type Specifier的表现之一。

        在 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;
         };
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2014-09-09
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多