C ++でのtypedefの前方宣言


235

コンパイラーがtypedefを転送宣言できないのはなぜですか?

それが不可能であると仮定すると、私の包含ツリーを小さく保つためのベストプラクティスは何ですか?

回答:


170

typedefを転送できます。しかしすること

typedef A B;

最初に転送宣言する必要がありますA

class A;

typedef A B;

11
技術的には「forward-typedef」を行うことができない(つまり、「typedef A;」を書くことができない)ため、最後に+1しますが、上記のトリックを使用して、OPが達成したいことをほぼ確実に達成できます。
j_random_hacker 2009

9
ただし、typedefが変更された場合、それらのすべての前方宣言も変更される可能性があることに注意してください。これは、古いおよび新しいtypedefが同じインターフェースの型を使用している場合、見落とす可能性があります。
数学

50
一般に、これは有用な解決策ではありません。たとえばtypedef、前方宣言を使用して複雑なマルチレベルテンプレートタイプに名前を付ける場合、この方法はかなり複雑で困難です。言うまでもなく、デフォルトのテンプレート引数に隠された実装の詳細に飛び込む必要があるかもしれません。そして、最終的な解決策は、元の型が非常に変更されやすい、長くて読めないコード(特に、型がさまざまな名前空間に由来する場合)です。
アダムバドゥーラ

3
また、これは「実装の詳細」を示しています(完全ではなくても...)が、フォワード宣言の背後にある考えはそれらを非表示にすることでした。
アダムBadura

3
@windfinder:それは行います:template <class T> class A; typedef A <C> B;
milianw 2013年

47

typedefを使用して定義されたCスタイルの構造体を前方宣言しようとしている私のようなあなたのために、いくつかのc ++コードで、次のような解決策を見つけました...

// a.h
 typedef struct _bah {
    int a;
    int b;
 } bah;

// b.h
 struct _bah;
 typedef _bah bah;

 class foo {
   foo(bah * b);
   foo(bah b);
   bah * mBah;
 };

// b.cpp
 #include "b.h"
 #include "a.h"

 foo::foo(bah * b) {
   mBah = b;
 }

 foo::foo(bah b) {
   mBah = &b;
 }

4
@LittleJohnこのソリューションの問題は、ダミー名_bahがパブリックAPIの一部と見なされないことです。フォワードデルケアファイルを参照してください。
user877329 2013

23

「fwdがtypedefを宣言する」には、クラスまたは構造体をfwd宣言する必要があり、次にtypedef宣言された型を使用できます。複数の同一のtypedefがコンパイラーによって受け入れられます。

長い形式:

class MyClass;
typedef MyClass myclass_t;

ショートフォーム:

typedef class MyClass myclass_t;

これは最も投票された質問とどのように違うのですか?stackoverflow.com/a/804956/931303
Jorge Leitao 2018

1
@JorgeLeitão違いがわかりませんか?その方法を1行で示しているわけではありません。
Pavel P

17

C ++(プレーンCではない)では、両方の定義が完全に同一である限り、型を2回typedefすることは完全に合法です。

// foo.h
struct A{};
typedef A *PA;

// bar.h
struct A;  // forward declare A
typedef A *PA;
void func(PA x);

// baz.cc
#include "bar.h"
#include "foo.h"
// We've now included the definition for PA twice, but it's ok since they're the same
...
A x;
func(&x);

34
メンテナンスいいえいいえ。この種のものは、遅かれ早かれ、カイザーに噛み付くでしょう。
Mark Storer

3
@MarkStorer、少なくともコンパイラは違いをキャッチしてエラーを生成します。これをVisual C ++で確認しました。
アラン

ニースですがAAは定義により空なので、このようにフィールドをどのように定義しますか?
Patrizio Bertoni 2017

10

型を宣言するには、そのサイズがわかっている必要があります。型へのポインタを転送宣言するか、型へのポインタをtypedefできます。

本当に必要な場合は、pimplイディオムを使用してインクルードを低く抑えることができます。しかし、ポインタではなく型を使用したい場合、コンパイラはそのサイズを知っている必要があります。

編集:j_random_hackerは重要な資格をこの回答に追加します。基本的に、型を使用するにはサイズを知っておく必要がありますが、型へのポインタまたは参照を作成するために型が存在することだけを知る必要がある場合は、前方宣言を行うことができます。タイプ。OPはコードを表示しませんでしたが、コンパイルできないと不平を言ったので、OPは単に参照するだけでなく、型を使用しようとしていると(おそらく正しく)想定しました。


35
まあ、クラス型の前方宣言は、サイズの知識なしにこれらの型を宣言します。また、そのような不完全な型へのポインタと参照を定義できることに加えて、パラメータを受け取ったり、そのような型の値を返したりする関数を宣言できます(ただし、定義はできません)。
j_random_hacker

3
申し訳ありませんが、これは適切な仮定ではありません。この答えは要点を外しています。これはtypedefの前方宣言の場合とほとんど同じです。
Cookie

6

前方宣言を使用する代わりにフルの#includeSは、あなたがしている場合のみ可能ですません(このファイルの範囲内)型自体が、それへのポインタまたは参照を使用して意図します。

タイプ自体を使用するには、コンパイラーがそのサイズを知っている必要があります-したがって、その完全な宣言を確認する必要があります-したがって、完全なもの#includeが必要です。

ただし、ポインターまたは参照のサイズは、指示先のサイズに関係なくコンパイラーに認識されているため、前方宣言で十分です。型識別子の名前を宣言します。

興味深いことに、ポインター、参照、classまたはstruct型を使用する場合、コンパイラーは不完全な型を処理できるため、先の型も転送宣言する必要がなくなります。

// header.h

// Look Ma! No forward declarations!
typedef class A* APtr; // class A is an incomplete type - no fwd. decl. anywhere
typedef class A& ARef;

typedef struct B* BPtr; // struct B is an incomplete type - no fwd. decl. anywhere
typedef struct B& BRef;

// Using the name without the class/struct specifier requires fwd. decl. the type itself.    
class C;         // fwd. decl. type
typedef C* CPtr; // no class/struct specifier 
typedef C& CRef; // no class/struct specifier 

struct D;        // fwd. decl. type
typedef D* DPtr; // no class/struct specifier 
typedef D& DRef; // no class/struct specifier 

2

同じ問題があり、異なるファイルに複数のtypedefをいじりたくなかったので、継承で解決しました。

だった:

class BurstBoss {

public:

    typedef std::pair<Ogre::ParticleSystem*, bool> ParticleSystem; // removed this with...

した:

class ParticleSystem : public std::pair<Ogre::ParticleSystem*, bool>
{

public:

    ParticleSystem(Ogre::ParticleSystem* system, bool enabled) : std::pair<Ogre::ParticleSystem*, bool>(system, enabled) {
    };
};

魅力のように働いた。もちろん、参照を変更する必要がありました

BurstBoss::ParticleSystem

単に

ParticleSystem

1

typedefusing具体的に)を継承とコンストラクター継承(?)に置き換えました。

元の

using CallStack = std::array<StackFrame, MAX_CALLSTACK_DEPTH>;

交換済み

struct CallStack // Not a typedef to allow forward declaration.
  : public std::array<StackFrame, MAX_CALLSTACK_DEPTH>
{
  typedef std::array<StackFrame, MAX_CALLSTACK_DEPTH> Base;
  using Base::Base;
};

このようにして、私は前方宣言CallStackをすることができました:

class CallStack;

0

Bill Kotsiasが述べたように、ポイントのtypedefの詳細を非公開にし、それらを前方宣言する唯一の合理的な方法は、継承を使用することです。ただし、C ++ 11を使用すれば、これを少しうまく行うことができます。このことを考慮:

// LibraryPublicHeader.h

class Implementation;

class Library
{
...
private:
    Implementation* impl;
};
// LibraryPrivateImplementation.cpp

// This annoyingly does not work:
//
//     typedef std::shared_ptr<Foo> Implementation;

// However this does, and is almost as good.
class Implementation : public std::shared_ptr<Foo>
{
public:
    // C++11 allows us to easily copy all the constructors.
    using shared_ptr::shared_ptr;
};

0

@BillKotsiasのように、私は継承を使用し、それは私のために働いた。

私はこの混乱を変更しました(私の宣言* .hにすべてのboostヘッダーが必要でした)

#include <boost/accumulators/accumulators.hpp>
#include <boost/accumulators/statistics.hpp>
#include <boost/accumulators/statistics/stats.hpp>
#include <boost/accumulators/statistics/mean.hpp>
#include <boost/accumulators/statistics/moment.hpp>
#include <boost/accumulators/statistics/min.hpp>
#include <boost/accumulators/statistics/max.hpp>

typedef boost::accumulators::accumulator_set<float,
 boost::accumulators::features<
  boost::accumulators::tag::median,
  boost::accumulators::tag::mean,
  boost::accumulators::tag::min,
  boost::accumulators::tag::max
 >> VanillaAccumulator_t ;
std::unique_ptr<VanillaAccumulator_t> acc;

この宣言に(* .h)

class VanillaAccumulator;
std::unique_ptr<VanillaAccumulator> acc;

実装(* .cpp)は

#include <boost/accumulators/accumulators.hpp>
#include <boost/accumulators/statistics.hpp>
#include <boost/accumulators/statistics/stats.hpp>
#include <boost/accumulators/statistics/mean.hpp>
#include <boost/accumulators/statistics/moment.hpp>
#include <boost/accumulators/statistics/min.hpp>
#include <boost/accumulators/statistics/max.hpp>

class VanillaAccumulator : public
  boost::accumulators::accumulator_set<float,
    boost::accumulators::features<
      boost::accumulators::tag::median,
      boost::accumulators::tag::mean,
      boost::accumulators::tag::min,
      boost::accumulators::tag::max
>>
{
};
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.