Vorwärtsdeklaration eines typedef in C ++

235

Warum lässt mich der Compiler kein typedef weiterleiten?

Angenommen, es ist unmöglich, was ist die beste Vorgehensweise, um meinen Einschlussbaum klein zu halten?

user96825
quelle

Antworten:

170

Sie können typedef weiterleiten. Aber zu tun

typedef A B;

Sie müssen zuerst weiterleiten A:

class A;

typedef A B;
Hong Jiang
quelle
11
+1 am Ende, denn während Sie technisch nicht "vorwärts-typedef" können (dh Sie können nicht "typedef A;" schreiben), können Sie mit ziemlicher Sicherheit das erreichen, was das OP mit Ihrem obigen Trick erreichen möchte.
j_random_hacker
9
Beachten Sie jedoch, dass Sie bei Änderungen des Typedef möglicherweise auch alle Vorwärtsdeklarationen ändern, die Sie möglicherweise übersehen, wenn das alte und das neue Typedef Typen mit derselben Schnittstelle verwenden.
Mathe
50
Im Allgemeinen ist dies keine nützliche Lösung. Wenn die typedefNamen beispielsweise einen komplexen mehrstufigen Vorlagentyp verwenden, der eine Vorwärtsdeklaration verwendet, ist dies auf diese Weise ziemlich komplex und schwierig. Ganz zu schweigen davon, dass möglicherweise Implementierungsdetails erforderlich sind, die in Standardvorlagenargumenten versteckt sind. Und die Endlösung ist ein langwieriger und unlesbarer Code (insbesondere wenn Typen aus verschiedenen Namespaces stammen), der sehr anfällig für Änderungen des ursprünglichen Typs ist.
Adam Badura
3
Auch dies zeigt "Implementierungsdetails" (auch wenn nicht vollständig, aber immer noch ...), während die Idee hinter der Vorwärtsdeklaration darin bestand, sie zu verbergen.
Adam Badura
3
@windfinder: Es tut: Vorlage <Klasse T> Klasse A; typedef A B;
Milianw
47

Für diejenigen unter Ihnen wie mich, die eine mit typedef definierte C-artige Struktur in einem C ++ - Code deklarieren möchten, habe ich eine Lösung gefunden, die wie folgt lautet ...

// 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;
 }
Kleiner John
quelle
4
@LittleJohn Das Problem bei dieser Lösung ist, dass der Dummy-Name _bah nicht als Teil der öffentlichen API betrachtet wird. Siehe Vorwärts-Delcare-DATEI.
user877329
23

Um "fwd einen Typedef deklarieren" zu können, müssen Sie eine Klasse oder eine Struktur fwd deklarieren, und dann können Sie den deklarierten Typedef eingeben. Mehrere identische Typedefs werden vom Compiler akzeptiert.

lange Form:

class MyClass;
typedef MyClass myclass_t;

Kurzform:

typedef class MyClass myclass_t;
Pavel P.
quelle
Wie unterscheidet sich das von der am häufigsten gestellten Frage? stackoverflow.com/a/804956/931303
Jorge Leitao
1
@ JorgeLeitão siehst du nicht wie es anders ist? Es zeigt nicht, wie das in einer Zeile gemacht wird.
Pavel P
17

In C ++ (aber nicht in einfachem C) ist es völlig legal, einen Typ zweimal zu tippen, solange beide Definitionen vollständig identisch sind:

// 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);
Adam Rosenfield
quelle
34
Wartung Nein Nein. So etwas wird Sie früher oder später in den Keister beißen.
Mark Storer
3
@MarkStorer, zumindest der Compiler erkennt einen Unterschied und generiert einen Fehler. Ich habe dies mit Visual C ++ überprüft.
Alan
Schön, aber wie definieren Sie AFelder auf diese Weise, da sie Aper Definition leer sind?
Patrizio Bertoni
10

Denn um einen Typ zu deklarieren, muss seine Größe bekannt sein. Sie können einen Zeiger auf den Typ weiterleiten oder einen Zeiger auf den Typ eingeben.

Wenn Sie wirklich wollen, können Sie die Pimpl-Sprache verwenden, um die Includes niedrig zu halten. Wenn Sie jedoch einen Typ anstelle eines Zeigers verwenden möchten, muss der Compiler seine Größe kennen.

Bearbeiten: j_random_hacker fügt dieser Antwort eine wichtige Qualifikation hinzu. Grundsätzlich muss die Größe bekannt sein, um den Typ verwenden zu können. Eine Vorwärtsdeklaration kann jedoch erfolgen, wenn nur bekannt sein muss, dass der Typ vorhanden ist , um Zeiger oder Verweise auf den Typ zu erstellen Art. Da die OP nicht Code zeigen tat, aber beschwerte sich wäre es nicht kompilieren, ging ich davon aus (wahrscheinlich richtig) , dass die OP versuchte , verwenden Sie die Art, nicht nur darauf verweisen.

tpdi
quelle
35
Vorwärtsdeklarationen von Klassentypen deklarieren diese Typen ohne Kenntnis ihrer Größe. Zusätzlich zur Definition von Zeigern und Verweisen auf solche unvollständigen Typen können Funktionen deklariert (aber nicht definiert) werden, die Parameter annehmen und / oder einen Wert solcher Typen zurückgeben.
j_random_hacker
3
Entschuldigung, ich denke nicht, dass das eine gute Annahme ist. Diese Antwort ist nebensächlich. Dies ist in hohem Maße der Fall, wenn eine Vorwärtsdeklaration typisiert wird.
Cookie
6

Die Verwendung von Vorwärtsdeklarationen anstelle eines vollständigen #includes ist nur möglich, wenn Sie nicht beabsichtigen, den Typ selbst (im Bereich dieser Datei), sondern einen Zeiger oder Verweis darauf zu verwenden.

Um den Typ selbst verwenden zu können, muss der Compiler seine Größe kennen - daher muss seine vollständige Deklaration gesehen werden - daher ist eine vollständige Deklaration #includeerforderlich.

Die Größe eines Zeigers oder einer Referenz ist dem Compiler jedoch unabhängig von der Größe des Pointees bekannt, sodass eine Vorwärtsdeklaration ausreichend ist - sie deklariert einen Typkennungsnamen.

Interessanterweise kann der Compiler bei Verwendung von Zeigern oder Verweisen auf classoder structTypen unvollständige Typen verarbeiten , sodass Sie die Pointee-Typen nicht weiter deklarieren müssen:

// 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 
Adi Shavit
quelle
2

Ich hatte das gleiche Problem, wollte mich nicht mit mehreren Typedefs in verschiedenen Dateien herumschlagen, also habe ich es mit Vererbung gelöst:

war:

class BurstBoss {

public:

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

tat:

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

public:

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

Lief wie am Schnürchen. Natürlich musste ich alle Referenzen von ändern

BurstBoss::ParticleSystem

zu einfach

ParticleSystem
Bill Kotsias
quelle
1

Ich habe die typedef( usingum genau zu sein) durch Vererbung und Konstruktorvererbung (?) Ersetzt .

Original

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

Ersetzt

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;
};

Auf diese Weise konnte ich deklarieren CallStackmit:

class CallStack;
Notinlist
quelle
0

Wie Bill Kotsias bemerkte, ist die einzige vernünftige Möglichkeit, die typedef-Details Ihres Punktes privat zu halten und sie weiterzuleiten, die Vererbung. Mit C ++ 11 können Sie es allerdings etwas besser machen. Bedenken Sie:

// 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;
};
Timmmm
quelle
0

Wie @BillKotsias habe ich Vererbung verwendet, und es hat bei mir funktioniert.

Ich habe dieses Durcheinander geändert (für das alle Boost-Header in meiner Deklaration * .h erforderlich waren).

#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;

in diese Erklärung (* .h)

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

und die Implementierung (* .cpp) war

#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
>>
{
};
Mark Lakata
quelle