未命名结构的前向声明

时间:2011-08-31 11:42:57

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

赏金问题:所以,这两个Foo不是一回事。精细。第二种形式在图书馆中给出。 如果我无法更改它,我如何正确声明它?


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

struct Foo;
typedef struct {} Foo;

这会出现以下错误:

  

'struct Foo'之前的声明为'struct Foo'

我想转发声明,但是它!这有什么不对?

12 个答案:

答案 0 :(得分:40)

您要声明两个名称相同的实体。第一个是struct Foo,是一个名为Foo的结构。第二个是匿名结构的别名。

如果你改为:

struct Foo;
struct Foo {};

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

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

答案 1 :(得分:38)

typedef-anonymous struct是一种在C ++ 03之前的实践,主要是为了保持与C99之前的编译器的兼容性。

鉴于这是2011年,并且C ++和C都发生了变化,我想知道为什么没有这样一个库的最新版本!

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

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

这样的前瞻声明
struct MyFoo;

并将其定义为

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

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

答案 2 :(得分:11)

在类似的情况下,我有一个像

这样的遗留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!

答案 3 :(得分:8)

您不需要在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形式。

答案 4 :(得分:5)

您的前瞻性声明声明会有一个名为struct的{​​{1}}。

您的第二个声明属于名为Foo的{​​{1}}。这些不是一回事。

答案 5 :(得分:2)

对于typedef的前向声明,你需要引用typedeffed的东西,如:

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

由于你想要转发声明一个匿名结构,你既不能在原始实体的前向声明中给它一个名字,也不能在键入它时引用它。 “逻辑”语法是:

struct ;
typedef bar;
class {};

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

为了达到标准,让我们来看看9.1-2:

  

仅由类密钥标识符组成的声明;是一个   重新声明当前范围内的名称或转发   将标识符声明为类名。它介绍了这门课程   命名为当前范围。

没有标识符,没有前瞻性声明。

最重要的一点是:避免使用匿名结构,除非它们为您提供了您真正需要的优势。

答案 6 :(得分:2)

恕我直言,只需将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声明时,此方法才有效。我不建议它。]

答案 7 :(得分:2)

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

使用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;

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

答案 8 :(得分:1)

为什么不直接拒绝前进。

如果第二个定义位于头文件中,则可以首先在C ++头文件中包含头文件。为了让C ++认为它是C头,在大多数情况下,将#include与extern "C" {}包含在一起就足够了。

答案 9 :(得分:1)

我认为我最近遇到了与原始海报相同的问题,并按照以下方式解决了问题:

我正在编写一个包含第三方提供的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;
}

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

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

希望这有帮助!

答案 10 :(得分:0)

你无法转发声明它,因为它未命名。它是一个未命名的结构,Foo是一个typedef。

答案 11 :(得分:0)

您正在尝试输入以前使用过的名称。该声明完全有效,只需要使用其他名称。

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

请注意,typedef不能以相同的名称使用。