web-dev-qa-db-ja.com

名前のない構造体の前方宣言

バウンティの質問:したがって、これらの2つのFoosは同じものではありません。いいね2番目の形式はライブラリで提供されます。 変更できない場合、どうすればそれを前方宣言できますか?


繰り返し定義がなければ、CとC++は繰り返し宣言を許可するといつも思っていました。次に、Cライブラリを拡張するC++コードを記述しようとしたときに、この問題に遭遇しました。

struct Foo;
typedef struct {} Foo;

これにより、次のエラーが発生します。

「struct Foo」には「struct Foo」として以前の宣言があります

私は前向きに宣言したい、それをくそー!ここで何が問題になっていますか?

42
spraff

typedef-ing anonymous structは、C++ 03より前の慣例であり、主にC99以前のコンパイラとの互換性を維持することを目的としています。

これが2011年であり、C++とCの両方が変更されていることを考えると、そのようなライブラリの最新バージョンがないのはなぜでしょうか。

それが開発中でなくなった場合、「去る」ことはできませんが、「存続」して変更するだけです。まだ展開中の場合は、問題を開発チームに送信します。

回避策が必要な場合は、構造体が継承できることを考慮してください。したがって、次のようなフォワード宣言を書きます

struct MyFoo;

そしてそれを次のように定義します

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

また、すべてのコードでFooを忘れて、常にMyFooを使用してください。

39

同じ名前の2つの異なるエンティティを宣言しています。最初、 struct FooFooという名前の構造体です。 2番目はanonymous構造体のエイリアスです。

代わりに行う場合:

struct Foo;
struct Foo {};

両方の状況でFooという名前の構造体を宣言しているため、機能します。

匿名構造体を転送宣言することはできません。 2つの選択肢があります。定義全体を含めるか、ヘッダーを変更して構造体に名前を付けます。

40

同様の状況で、次のようなレガシー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 {};

この方法では、Fooの代わりにC :: Fooを透過的に使用し、MyFooは必要ありません。

12
Alex Cohn

C++では構造体をtypedefする必要はありません。

struct Foo;     // Forward declaration

struct Foo 
{

}; // Definition

Cでstruct Fooの代わりにFooだけを呼び出す場合は、doが必要です。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という形式を使用できます。

8
molbdnilo

前方宣言では、structというFooが存在することを宣言しています。

2番目の宣言は、typedefというFooです。これらは同じものではありません。

5

私見、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には、struct宣言が1つだけ含まれています。私お勧めしませんそれ。]

2
iammilind

あなたの特定のコンパイラはここで違いを生むかもしれません。

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
Unsigned

Typedefの前方宣言の場合、次のようにtypedeffされているものを参照する必要があります。

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

匿名構造体を前方宣言したいので、元のエンティティの前方宣言で名前を付けることも、型定義するときに参照することもできません。 「論理」構文は次のようになります。

struct ;
typedef bar;
class {};

しかし、これは明らかに不可能であるため、匿名の構造体を転送宣言することはできません。

標準化するために、9.1-2を見てみましょう。

クラスキー識別子のみで構成される宣言。現在のスコープ内の名前の再宣言、またはクラス名としての識別子の前方宣言です。クラス名を現在のスコープに導入します。

識別子なし、前方宣言なし。

つまり、本当に必要なアドバンテージを提供しない限り、匿名の構造体は避けてください。

2
PlasmaHH

私は最近、元のポスターと同じ問題に遭遇し、以下のように解決したと思います:

次のように定義されたサードパーティ提供の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;

お役に立てれば!

1
Ken Griggs

前方宣言を避けてみませんか。

2番目の定義がヘッダーファイルにある場合は、最初にヘッダーファイルをC++ヘッダーファイルに含めることができます。 C++がそれをCヘッダーと見なすようにするには、extern "C" {および}で#includeを使用することで、ほとんどの場合十分です。

0
shr

以前に使用した名前をtypedefしようとしています。ステートメントは完全に有効です。別の名前を使用する必要があるだけです。

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

Typedefは同じ名前で使用できないことに注意してください。

0
jagbandhuster

名前が付いていないので、それを転送宣言することはできません。名前のない構造体で、Fooはtypedefです。

0
K-ballo