Подтвердить что ты не робот

Вперед объявления неназванной структуры

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


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

struct Foo;
typedef struct {} Foo;

Это приводит к следующей ошибке:

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

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

4b9b3361

Ответ 1

typedef-ing anonymous struct - это практика, которая предшествует С++ 03 и в основном ориентирована на сохранение совместимости с компиляторами pre-C99.

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

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

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

struct MyFoo;

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

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

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

Ответ 2

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

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

struct Foo;
struct Foo {};

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

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

Ответ 3

Вам не нужно создавать typedef-структуры в С++:

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, так и в С++.

Ответ 4

Ваше декларативное объявление объявляет, что будет struct называется Foo.

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

Ответ 5

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

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

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

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!

Ответ 6

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

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

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

struct ;
typedef bar;
class {};

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

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

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

Нет идентификатора, нет декларации о переходе.

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

Ответ 7

IMHO, просто измените свой typedef на,

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

Нет вреда, и он по-прежнему будет совместим как на C, так и на С++. Теперь вы можете переслать объявление.

[Примечание. Если вы все еще настаиваете на том, чтобы не касаться typedef вообще, вот вот грязный трюк.

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

Это будет работать, только если Foo.h содержит только объявление 1 struct. Я не рекомендую.]

Ответ 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

Почему бы вам просто не переслать декларацию.

Если второе определение было в файле заголовка, вы можете включить заголовочный файл сначала в заголовочный файл С++. Для того, чтобы С++ считал его заголовком C, в большинстве случаев охват #include с extern "C" { и } должен быть достаточным.

Ответ 10

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

Я пишу обертку вокруг стороннего предоставленного 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'd. Обратите внимание, что критически важно, чтобы структура не была анонимной, как это обычно бывает в более раннем C-коде:

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

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

Ответ 11

Вы не можете переадресовать объявление, так как его неназванный. Его неназванная структура, для которой Foo является typedef.

Ответ 12

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

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

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