¿Por qué el compilador no me permite declarar un typedef?
Suponiendo que sea imposible, ¿cuál es la mejor práctica para mantener pequeño mi árbol de inclusión?
¿Por qué el compilador no me permite declarar un typedef?
Suponiendo que sea imposible, ¿cuál es la mejor práctica para mantener pequeño mi árbol de inclusión?
Respuestas:
Puede reenviar typedef. Pero para hacer
typedef A B;
primero debe reenviar declarar A
:
class A;
typedef A B;
typedef
nombra un tipo de plantilla complejo de varios niveles que utiliza una declaración directa de esta manera, es bastante complejo y difícil. Sin mencionar que podría requerir sumergirse en detalles de implementación ocultos en los argumentos de plantilla predeterminados. Y la solución final es un código extenso e ilegible (especialmente cuando los tipos provienen de varios espacios de nombres) muy propensos a cambiar en el tipo original.
Para aquellos de ustedes como yo, que buscan declarar hacia adelante una estructura de estilo C que se definió usando typedef, en algún código de C ++, he encontrado una solución que sigue de la siguiente manera ...
// 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;
}
Para "fwd declarar un typedef" necesita fwd declarar una clase o una estructura y luego puede escribir typedef declarado tipo. Múltiples definiciones de tipo idénticas son aceptables por el compilador.
forma larga:
class MyClass;
typedef MyClass myclass_t;
forma corta:
typedef class MyClass myclass_t;
En C ++ (pero no en C simple), es perfectamente legal escribir un tipo dos veces, siempre y cuando ambas definiciones sean completamente idénticas:
// 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);
A
campos de esta manera ya que A
está vacío por definición?
Porque para declarar un tipo, su tamaño necesita ser conocido. Puede reenviar declarar un puntero al tipo, o typedef un puntero al tipo.
Si realmente quieres, puedes usar el modismo de pimpl para mantener bajas las inclusiones. Pero si desea utilizar un tipo, en lugar de un puntero, el compilador debe conocer su tamaño.
Editar: j_random_hacker agrega una calificación importante a esta respuesta, básicamente que se necesita saber el tamaño para usar el tipo, pero se puede hacer una declaración hacia adelante si solo necesitamos saber que el tipo existe , para crear punteros o referencias al tipo. Como el OP no mostró código, pero se quejó de que no se compilaría, supuse (probablemente correctamente) que el OP estaba tratando de usar el tipo, no solo referirme a él.
El uso de declaraciones adelantadas en lugar de un total de #include
s es posible sólo cuando se está sin la intención de utilizar el tipo en sí (en el ámbito de este archivo), pero un puntero o referencia a ella.
Para usar el tipo en sí, el compilador debe conocer su tamaño, por lo tanto, debe verse su declaración completa, por lo tanto, #include
se necesita un completo .
Sin embargo, el compilador conoce el tamaño de un puntero o referencia, independientemente del tamaño del puntero, por lo que una declaración directa es suficiente: declara un nombre de identificador de tipo.
Curiosamente, cuando se usa puntero o referencia a class
o struct
tipos, el compilador puede manejar tipos incompletos ahorrándole la necesidad de declarar también los tipos de puntas:
// 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
Tuve el mismo problema, no quería meterme con múltiples typedefs en diferentes archivos, así que lo resolví con la herencia:
estaba:
class BurstBoss {
public:
typedef std::pair<Ogre::ParticleSystem*, bool> ParticleSystem; // removed this with...
hizo:
class ParticleSystem : public std::pair<Ogre::ParticleSystem*, bool>
{
public:
ParticleSystem(Ogre::ParticleSystem* system, bool enabled) : std::pair<Ogre::ParticleSystem*, bool>(system, enabled) {
};
};
Trabajado como un encanto. Por supuesto, tuve que cambiar cualquier referencia de
BurstBoss::ParticleSystem
simplemente
ParticleSystem
Reemplacé el typedef
( using
para ser específico) con herencia y herencia de constructor (?).
Original
using CallStack = std::array<StackFrame, MAX_CALLSTACK_DEPTH>;
Sustituido
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 esta manera pude reenviar la declaración CallStack
con:
class CallStack;
Como señaló Bill Kotsias, la única forma razonable de mantener privados los detalles de typedef de su punto, y declararlos de antemano es mediante herencia. Sin embargo, puedes hacerlo un poco mejor con C ++ 11. Considera esto:
// 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;
};
Al igual que @BillKotsias, utilicé la herencia, y funcionó para mí.
Cambié este desastre (que requería todos los encabezados de impulso en mi declaración * .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;
en esta declaración (* .h)
class VanillaAccumulator;
std::unique_ptr<VanillaAccumulator> acc;
y la implementación (* .cpp) fue
#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
>>
{
};