Dichiarazione diretta di un typedef in C ++


235

Perché il compilatore non mi lascia inoltrare dichiarare un typedef?

Supponendo che sia impossibile, qual è la migliore pratica per mantenere piccolo il mio albero dell'inclusione?

Risposte:


170

Puoi fare il typedef in avanti. Ma da fare

typedef A B;

devi prima dichiarare A:

class A;

typedef A B;

11
+1 alla fine perché mentre tecnicamente non puoi "inoltrare-typedef" (cioè non puoi scrivere "typedef A;"), puoi quasi certamente realizzare ciò che l'OP vuole realizzare usando il tuo trucco sopra.
j_random_hacker,

9
Tuttavia, se il typedef cambia, è possibile modificare anche tutte quelle dichiarazioni forward, che potrebbero mancare se il vecchio e il nuovo typedef utilizzano tipi con la stessa interfaccia.
matematica

50
In generale questa non è una soluzione utile. Ad esempio, se i typedefnomi di un tipo di modello multilivello complesso che utilizza una dichiarazione diretta in questo modo è piuttosto complesso e difficile. Per non parlare del fatto che potrebbe essere necessario immergersi nei dettagli dell'implementazione nascosti negli argomenti del modello predefinito. E la soluzione finale è un codice lungo e illeggibile (specialmente quando i tipi provengono da vari spazi dei nomi) molto incline a cambiare nel tipo originale.
Adam Badura,

3
Anche questo mostra "dettagli di implementazione" (anche se non completamente ma comunque ...) mentre l'idea alla base della dichiarazione anticipata era di nasconderli.
Adam Badura,

3
@windfinder: lo fa: template <class T> class A; typedef A <C> B;
milianw,

47

Per quelli come voi, che non vedono l'ora di dichiarare una struttura in stile C che è stata definita usando typedef, in un codice c ++ ho trovato una soluzione che procede come segue ...

// 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 Il problema con questa soluzione è che il nome fittizio _bah non è considerato parte dell'API pubblica. Vedere il file forward delcare.
user877329

23

Per "dichiarare un typedef" devi dichiarare una classe o una struttura e quindi puoi digitare il tipo dichiarato. Numerosi typedef identici sono accettabili dal compilatore.

forma lunga:

class MyClass;
typedef MyClass myclass_t;

forma breve:

typedef class MyClass myclass_t;

In che modo si differenzia dalla domanda più votata? stackoverflow.com/a/804956/931303
Jorge Leitao

1
@ JorgeLeitão non vedi come è diverso? Non mostra come farlo in una riga.
Pavel P

17

In C ++ (ma non in semplice C), è perfettamente legale digitare un tipo due volte, a condizione che entrambe le definizioni siano completamente identiche:

// 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
Manutenzione No No. Questo genere di cose ti morderà nel box prima o poi.
Mark Storer

3
@MarkStorer, almeno il compilatore rileva qualsiasi differenza e genera un errore. Ho verificato questo con Visual C ++.
Alan

Bello, ma come si definiscono i Acampi in questo modo poiché Aè vuoto per definizione?
Patrizio Bertoni,

10

Perché per dichiarare un tipo, è necessario conoscerne le dimensioni. È possibile inoltrare un puntatore al tipo o digitare un puntatore al tipo.

Se vuoi davvero, puoi usare l'idioma del pimpl per tenere giù le inclusioni. Ma se vuoi usare un tipo, piuttosto che un puntatore, il compilatore deve conoscere le sue dimensioni.

Modifica: j_random_hacker aggiunge una qualifica importante a questa risposta, fondamentalmente che la dimensione deve essere conosciuta per usare il tipo, ma una dichiarazione diretta può essere fatta se dobbiamo solo sapere che il tipo esiste , al fine di creare puntatori o riferimenti al genere. Poiché l'OP non mostrava il codice, ma si lamentava che non si sarebbe compilato, ho ipotizzato (probabilmente correttamente) che l'OP stesse cercando di usare il tipo, non solo di fare riferimento ad esso.


35
Bene, le dichiarazioni a termine dei tipi di classe dichiarano questi tipi senza conoscere le loro dimensioni. Inoltre, oltre a poter definire puntatori e riferimenti a tali tipi incompleti, è possibile dichiarare (ma non definire) funzioni che accettano parametri e / o restituiscono un valore di tali tipi.
j_random_hacker il

3
Scusa, non penso sia una buona ipotesi. Questa risposta è accanto al punto. Questo è molto il caso di scrivere una dichiarazione a termine.
Cookie del

6

Utilizzando le dichiarazioni previsionali , invece di un pieno di #includes è possibile solo quando si è non intende utilizzare il tipo stesso (nell'ambito di questo file) ma un puntatore o un riferimento ad esso.

Per usare il tipo stesso, il compilatore deve conoscere le sue dimensioni - quindi deve essere vista la sua dichiarazione completa - quindi #includeè necessario un completo .

Tuttavia, la dimensione di un puntatore o di un riferimento è nota al compilatore, indipendentemente dalla dimensione della punta, quindi una dichiarazione diretta è sufficiente: dichiara un nome identificativo del tipo.

È interessante notare che quando si utilizza il puntatore o il riferimento a classo structtipi, il compilatore può gestire tipi incompleti risparmiando la necessità di inoltrare anche i tipi di punte:

// 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

Ho avuto lo stesso problema, non volevo scherzare con più typedef in file diversi, quindi l'ho risolto con l'ereditarietà:

era:

class BurstBoss {

public:

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

ha fatto:

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

public:

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

Ha funzionato come un fascino. Naturalmente, ho dovuto cambiare qualsiasi riferimento da

BurstBoss::ParticleSystem

semplicemente

ParticleSystem

1

Ho sostituito il typedef( usingper essere precisi) con l'ereditarietà e l'ereditarietà del costruttore (?).

Originale

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

sostituito

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

In questo modo sono stato in grado di inoltrare la dichiarazione CallStackcon:

class CallStack;

0

Come notato da Bill Kotsias, l'unico modo ragionevole per mantenere privati ​​i dettagli del tuo punto e inoltrarli è ereditario. Puoi farlo un po 'meglio con C ++ 11. Considera questo:

// 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

Come @BillKotsias, ho usato l'ereditarietà e ha funzionato per me.

Ho cambiato questo pasticcio (che ha richiesto tutte le intestazioni boost nella mia dichiarazione * .h)

#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 questa dichiarazione (* .h)

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

e l'implementazione (* .cpp) era

#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
>>
{
};
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.