【问题标题】:Forward declarations of unnamed struct未命名结构的前向声明
【发布时间】:2011-08-31 11:42:57
【问题描述】:

悬赏问题:所以,这两个Foos 不是一回事。美好的。第二种形式在图书馆给出。 鉴于我无法更改它,我该如何前向声明它?


我一直认为 C 和 C++ 允许重复声明,前提是没有重复定义。然后我在尝试编写扩展 C 库的 C++ 代码时遇到了这个问题。

struct Foo;
typedef struct {} Foo;

这会产生以下错误:

'struct Foo' 之前有一个声明为'struct Foo'

我要转发声明,该死!这里有什么问题?

【问题讨论】:

  • 什么是编译器?另外,空的声明可能会打扰它?
  • 没有一种语言叫做“C/C++”,尤其是在结构的上下文中。有太多所谓的 C++ 编写者在他们的代码中乱扔“结构”,就像 1989 年一样。
  • @Kerrek 在 C++ 代码中 struct 没有任何问题,我用它来表示我正在编写一个未封装的 POD 类型。
  • @spraff:我认为 Kerrek 指的是 C 的“习语”,即不将 structs 隐藏在 typedef 后面,而是做类似 void foo(struct MyStruct *p); 之类的事情。
  • @Oli:没错。还有诸如 typedef 定义之类的东西,typedef struct Foo { /* ... /* } Foo;.

标签: c++ c struct typedef forward-declaration


【解决方案1】:

typedef 匿名结构是 C++03 之前的一种实践,主要是为了保持与 C99 之前的编译器的兼容性。

鉴于这是 2011 年,而且 C++ 和 C 都发生了变化,我想知道为什么没有更新版本的此类库!

如果它不再处于开发中,你不能“离开”,而只是“生存”并改变它是这样做的方式。 如果仍在部署中,请将问题提交给开发团队。

如果您需要解决方法,请考虑 struct 可以继承。 所以,写一个像

这样的前向声明
struct MyFoo;

并将其定义为

#include "old_library.h"
struct MyFoo: public Foo {};

在所有代码中,忘记Foo,始终使用MyFoo

【讨论】:

  • 在类似的情况下,我有一个遗留的 C 标头,其中包含 == old_library.h == typedef struct { int data; } 富; == /old_library.h == 我在自己的 C++ 类中使用它,作为私有方法的参数:class C { void run(Foo *ds); ... } 为了避免 C.hpp 中的 #include "old_library.h",我使用以下前向声明:class C { struct Foo;无效运行(Foo *ds); ... } 在 C.cpp 中我有以下语句: extern "C" { #include "old_library.h" } struct C::Foo : public ::Foo {};这样,我透明地使用 C::Foo 而不是 Foo,并且不需要 MyFoo!
  • “鉴于这是 2011 年,而且 C++ 和 C 都发生了变化,我想知道为什么没有更新版本的此类库!”可能是因为 Windows 上 C 和 C++ 代码的事实上的标准编译器 MSVC++,现在在 2014 年仍然不支持 C99,而且不太可能很快支持它。
【解决方案2】:

您声明了两个具有相同名称的不同实体。第一个 struct Foo 是一个名为 Foo 的结构。第二个是 anonymous 结构的别名。

如果你这样做:

struct Foo;
struct Foo {};

它有效,因为您在两种情况下都声明了一个名为 Foo 的结构。

您不能转发声明匿名结构。您有两个选择:包含整个定义,或更改标题并命名结构。

【讨论】:

  • 对于 OP,你也不能前向声明 typedefs。
【解决方案3】:

在类似的情况下,我有一个旧的 C 标头,类似于

== old_library.h ==
typedef struct { int data; } Foo;
== /old_library.h ==

我在自己的 C++ 类中使用它,作为私有方法的参数:

class C {
  void run(Foo *ds);
  ...
}

为了避免 C.hpp 中的 #include "old_library.h",我使用以下前向声明:

class C {
  struct Foo;
  void run(Foo *ds);
  ...
}

在 C.cpp 中我有以下语句:

extern "C" {
#include "old_library.h"
}
struct C::Foo : public ::Foo {};

这样,我透明地使用 C::Foo 而不是 Foo,并且不需要 MyFoo!

【讨论】:

    【解决方案4】:

    您不需要在 C++ 中键入定义结构:

    struct Foo;     // Forward declaration
    
    struct Foo 
    {
    
    }; // Definition
    

    如果您只想在 C 中将其称为 Foo 而不是 struct Foo,您确实需要 typedef,这也可以通过不同的方式完成:

    struct Foo;     /* Forward declaration */
    
    struct Foo /* The name is needed here */
    {
    
    }; /* Definition */
    typedef struct Foo Foo;  /* typedef */
    

    struct Foo;     /* Forward declaration */
    
    typedef struct Foo /* The name is needed here */
    {
    
    } Foo; /* Definition and typedef combined */
    

    您当然可以在 C 和 C++ 中使用 struct Foo 形式。

    【讨论】:

    • 在 C 中,你不需要 typedef;您可以将类型称为struct Foo。 (我们中的一些人更喜欢这样做。)
    • @KeithThompson:单独的 typedef 也可以在将结构定义为前向声明之前完成。
    【解决方案5】:

    您的前向声明声明将有一个名为Foostruct

    您的第二个声明是一个名为Footypedef。这些不是一回事。

    【讨论】:

      【解决方案6】:

      对于 typedef 的前向声明,您需要引用正在被 typedef 的东西,例如:

      struct foo;
      typedef foo bar;
      class foo{};
      

      由于要前向声明一个匿名结构,既不能在原实体的前向声明中给它起名字,也不能在typedef的时候引用它。 “逻辑”语法是:

      struct ;
      typedef bar;
      class {};
      

      但是由于这显然是不可能的,所以不能转发声明匿名结构。

      要标准化,让我们看一下 9.1-2:

      仅由类键标识符组成的声明;要么是 在当前范围内重新声明名称或转发 将标识符声明为类名。它介绍了类 名称到当前范围内。

      没有标识符,没有前向声明。

      底线:避免匿名结构,除非它们能给你带来你真正需要的优势。

      【讨论】:

      • 有时匿名结构体带有遗留的头文件,并且弄乱一些旧的 3rd 方库头文件是个坏主意。解决方案可以追溯到 2001 年 [groups.google.com/group/comp.lang.c++.moderated/browse_thread/…:您不能“按原样”从未命名的结构转发声明,但您可以从它继承,并转发声明您的派生类!
      【解决方案7】:

      恕我直言,只需将您的 typedef 更改为,

      typedef struct Foo {} Foo;
                    ^^^^^
      

      没有任何害处,并且在 C 和 C++ 中仍然兼容。现在你可以转发声明了。

      [注意:如果你仍然坚持不碰typedef,那么这里就是肮脏的把戏

      struct Foo;
      #define struct struct Foo
      #include"Foo.h"  // contains typedef struct {} Foo;
      #undef struct
      

      只有当 Foo.h 仅包含 1 个 struct 声明时,这才有效。我不推荐。]

      【讨论】:

      • 我同意,我将使用的小改动:typedef struct Foo_struct {} Foo; 避免了一些较旧的 C 编译器中的名称冲突,并且在试图找出奇怪的编译错误时通常更清楚(是它是结构或 typedef 失败的东西,C++ 编译器没有这个问题,但我认为它仍然被认为是好的形式)。
      【解决方案8】:

      您的特定编译器可能会有所不同。

      使用 MinGW GCC 3.4.5,两个声明都编译时没有错误或警告(使用 -Wall

      struct Foo;
      typedef struct {} Foo;
      

      struct Foo;
      typedef struct Foo {} Foo;
      

      库中是否可能已经存在前向声明?例如,允许循环指针:

      struct Foo;
      typedef struct Foo {
          struct Foo *fooPtr;
      } Foo;
      

      如果这已经存在于库头文件中,则会导致您描述的错误。

      【讨论】:

        【解决方案9】:

        我想我最近遇到了与原始发帖人相同的问题,并已解决如下:

        我正在围绕第三方提供的 API 编写一个包装器,定义为:

        Foo.h:

        typedef struct _foo 
        {
            int i;
        } Foo;
        
        Foo* MakeFoo();
        int UseFoo(Foo* foo);
        

        我的包装器需要 Foo 作为成员,但我不想将 Foo 暴露给我的包装器的所有消费者。

        UberFoo.h:

        #pragma once
        
        struct _foo;  // forward declare the actual type _foo, not the typedef Foo
        
        class UberFoo
        {
        public:
            UberFoo();
            int GetAnswer();
        private:
            _foo* f;
        };
        

        UberFoo.cpp:

        #include "UberFoo.h"
        #include "Foo.h"
        
        UberFoo::UberFoo()
        {
            this->f = MakeFoo();
        }
        
        int UberFoo::GetAnswer()
        {
            return UseFoo(f);
        }
        

        现在,我的类的使用者可以实例化它,而无需访问 _foo/Foo 的实际定义。如果我需要将指向 _foo 的指针作为参数传递或从函数返回它们以及拥有一个成员 _foo,这也将起作用。

        main.cpp:

        #include <cstdio>
        #include "UberFoo.h"
        
        int main(int argc, char* argv[])
        {
            UberFoo u;
            printf( "The Answer = %d\n", u.GetAnswer());
            return 0;
        }
        

        这里的技巧是转发声明实际的结构类型,而不是 typedef 的名称。请注意,结构不能是匿名的,这在旧 C 代码中很常见:

        typedef struct // the actual struct type underlying the typedef is anonymous here
        {
            int i;
        } ThisWontWork;
        

        希望这会有所帮助!

        【讨论】:

          【解决方案10】:

          您不能转发声明它,因为它没有命名。它是一个未命名的结构,Foo 是一个 typedef。

          【讨论】:

          【解决方案11】:

          你为什么不直接避免前向decl。

          如果第二个定义在头文件中,则可以首先将头文件包含在 C++ 头文件中。为了让 C++ 将其视为 C 标头,在大多数情况下,将 #include 与 extern "C" {} 结合起来就足够了。

          【讨论】:

            【解决方案12】:

            您正在尝试键入定义以前使用的名称。该语句完全有效,只是您需要使用不同的名称。

            struct Foo; // Forward declaration of struct Foo
            typedef struct {} anotherFoo; // Another structure typedefed
            struct Foo {
             // Variables
            }; // Definition of the forward declared Foo.
            

            注意typedef不能用在同一个名字里。

            【讨论】:

              猜你喜欢
              • 2012-06-09
              • 2010-10-11
              • 1970-01-01
              • 1970-01-01
              • 2018-11-06
              • 2012-12-15
              • 2017-04-05
              • 2016-07-14
              • 1970-01-01
              相关资源
              最近更新 更多