web-dev-qa-db-fra.com

Déclaration en avant d'un typedef en C ++

Pourquoi le compilateur ne me permet-il pas de déclarer une typedef?

En supposant que ce soit impossible, quelle est la meilleure pratique pour garder mon arbre d'inclusion petit?

218
user96825

Vous pouvez faire typedef avant. Mais à faire

typedef A B;

vous devez d'abord transmettre déclarer A:

class A;

typedef A B;
158
Hong Jiang

Pour ceux d'entre vous qui, comme moi, cherchent à déclarer une structure de style C définie à l'aide de typedef, dans du code c ++, j'ai trouvé une solution qui va comme suit ...

// 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;
 }
44
LittleJohn

Pour "fwd déclarer un typedef", vous devez fwd déclarer une classe ou une structure et vous pouvez ensuite typedef déclarer le type. Plusieurs typedefs identiques sont acceptables par le compilateur.

forme longue:

class MyClass;
typedef MyClass myclass_t;

forme abrégée:

typedef class MyClass myclass_t;
18
Pavel

En C++ (mais pas en C), il est parfaitement légal de typer deux fois un type, tant que les deux définitions sont complètement identiques:

// 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);
17
Adam Rosenfield

Parce que pour déclarer un type, sa taille doit être connue. Vous pouvez transférer un pointeur au type ou transférer un pointeur au type.

Si vous le souhaitez vraiment, vous pouvez utiliser le langage pimpl pour limiter les inclusions. Mais si vous voulez utiliser un type plutôt qu'un pointeur, le compilateur doit connaître sa taille.

Edit: j_random_hacker ajoute une qualification importante à cette réponse, en gros que la taille doit être connue pour tiliser le type, mais une déclaration en aval peut être faite s'il ne faut que connaître le type existe, afin de créer des pointeurs ou des références au type. Puisque l’OP n’affiche pas le code, mais s’est plaint de ne pas compiler, j’ai supposé (probablement correctement) que l’OP essayait de tiliser le type, pas seulement y faire référence.

10
tpdi

Utiliser les déclarations forward à la place d'un #includes complet est possible uniquement lorsque not = veut utiliser le type lui-même (dans l'étendue de ce fichier) mais un pointeur ou une référence à celui-ci.

Pour utiliser le type lui-même, le compilateur doit connaître sa taille. Par conséquent, sa déclaration complète doit être visible. Par conséquent, un #include complet est requis.

Cependant, la taille d'un pointeur ou d'une référence est connue du compilateur, quelle que soit la taille de la pointee; une déclaration en aval suffit donc - il déclare un nom d'identificateur de type.

Il est intéressant de noter que, lorsque vous utilisez un type de pointeur ou une référence à class ou struct, le compilateur peut gérer types incomplets, ce qui vous évite de devoir également déclarer les types pointee:

// 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 
6
Adi Shavit

J'avais le même problème, je ne voulais pas jouer avec plusieurs typedefs dans différents fichiers, je l'ai donc résolu par héritage:

était:

class BurstBoss {

public:

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

fait:

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

public:

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

Travaillé comme un charme. Bien sûr, je devais changer les références de

BurstBoss::ParticleSystem

simplement

ParticleSystem
2
Bill Kotsias

Comme l'a noté Bill Kotsias, le seul moyen raisonnable de garder les détails de typedef de votre point confidentiels et de les déclarer en aval consiste à utiliser l'héritage. Vous pouvez cependant le faire un peu mieux avec C++ 11. Considère ceci:

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

J'ai remplacé le typedef (using pour être spécifique) par héritage et héritage constructeur (?).

Original

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

Remplacé

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

De cette façon, j'ai pu transmettre déclarer CallStack avec:

class CallStack;
0
Notinlist

Comme @BillKotsias, j'ai utilisé l'héritage et cela a fonctionné pour moi.

J'ai changé ce gâchis (qui nécessitait tous les en-têtes de boost dans ma déclaration * .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;

dans cette déclaration (* .h)

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

et la mise en œuvre (* .cpp) était

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