Форвардные объявления безымянной структуры - PullRequest
41 голосов
/ 31 августа 2011

Вопрос о награде: Итак, эти два Foo не одно и то же. Хорошо. Вторая форма дается в библиотеке. Как мне переслать или объявить, если я не могу его изменить?


Я всегда думал, что C и C ++ допускают повторные объявления при условии, что повторных определений не было. Затем я столкнулся с этой проблемой, когда пытался написать код на C ++, который расширяет библиотеку C.

struct Foo;
typedef struct {} Foo;

Это дает следующую ошибку:

«struct Foo» имеет предыдущее объявление как «struct Foo»

Я хочу выдвинуть-объявить, черт возьми! Что здесь не так?

Ответы [ 12 ]

40 голосов
/ 31 августа 2011

Вы объявляете две разные сущности с одинаковыми именами.Первая, struct Foo, является структурой с именем Foo.Второй - псевдоним для анонимной структуры.

Если вы сделаете вместо:

struct Foo;
struct Foo {};

Это работает, потому что вы объявляете структуру с именем Fooв обеих ситуациях.

Вы не можете пересылать объявления анонимных структур.У вас есть два варианта: включить полное определение или изменить заголовок и назвать структуру.

38 голосов
/ 05 сентября 2011

typedefing - анонимная структура - это практика, предшествующая C ++ 03 и ориентированная в основном на сохранение совместимости с компиляторами до C99.

Учитывая, что это 2011 год, и что C ++ и Cменяются, интересно, почему нет более современной версии такой библиотеки!

Если она больше не находится в разработке, вы не можете «уйти», а просто «выжить» и изменить ее.способ сделать это.Если вы все еще находитесь в развертывании, отправьте проблему в группу разработчиков.

Если вам нужен обходной путь, учтите, что структура может наследоваться.Итак, напишите предварительную декларацию типа

struct MyFoo;

и определите ее как

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

И во всем своем коде забудьте о Foo и всегда используйте MyFoo.

11 голосов
/ 12 января 2012

В аналогичной ситуации у меня есть устаревший заголовок C с чем-то вроде

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

Я использую его в своем собственном классе C ++, в качестве параметра для частного метода:

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

Чтобы избежать #include "old_library.h" из C.hpp, я использую следующее предварительное объявление:

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!

8 голосов
/ 31 августа 2011

Вам не нужно печатать структуры конструкций в C ++:

struct Foo;     // Forward declaration

struct Foo 
{

}; // Definition

Если вы хотите назвать его просто Foo вместо struct Foo в C, вам нужно 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 */

Конечно, вы можете использовать форму struct Foo на C и C ++.

5 голосов
/ 31 августа 2011

Ваша предварительная декларация заявляет, что будет struct с именем Foo.

Ваше второе объявление имеет typedef с именем Foo.Это не одно и то же.

2 голосов
/ 07 сентября 2011

Ваш конкретный компилятор может изменить ситуацию.

При использовании 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;

Если это уже существует в заголовках библиотеки, это приведет к описанной вами ошибке.

2 голосов
/ 03 сентября 2011

ИМХО, просто поменяйте 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 объявление. Я не рекомендую это.]

2 голосов
/ 03 сентября 2011

Для прямого объявления typedef вам нужно сослаться на объект, который определен как typedeffed, например:

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

Так как вы хотите переслать объявление анонимной структуры, вы не можете дать ееимя в предварительном объявлении исходного объекта, и вы не можете ссылаться на него при определении типа.«Логический» синтаксис будет выглядеть следующим образом:

struct ;
typedef bar;
class {};

Но поскольку это, очевидно, невозможно, вы не можете пересылать объявления анонимных структур.

Чтобы перейти на стандарт, давайте взглянем на 9.1-2:

Объявление, состоящее исключительно из идентификатора ключа класса;это либо переопределение имени в текущей области, либо предварительное объявление идентификатора в качестве имени класса.Он вводит имя класса в текущую область.

Нет идентификатора, нет предварительного объявления.

Суть этого: избегайте анонимных структур, если они не дают вам преимущества, которые вам действительно нужны.

1 голос
/ 10 февраля 2012

Я думаю, что недавно столкнулся с той же проблемой, что и оригинальный постер, и решил ее следующим образом:

Я пишу оболочку вокруг стороннего 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;

Надеюсь, это поможет!

0 голосов
/ 09 сентября 2011

Вы пытаетесь ввести ранее использованное имя.Это утверждение совершенно правильно, только вам нужно использовать другое имя.

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

Обратите внимание, что typedef нельзя использовать с тем же именем.

...