Dlaczego kompilator nie pozwala mi na przekazywanie deklaracji typedef?
Zakładając, że to niemożliwe, jaka jest najlepsza praktyka utrzymywania małego drzewa integracji?
Dlaczego kompilator nie pozwala mi na przekazywanie deklaracji typedef?
Zakładając, że to niemożliwe, jaka jest najlepsza praktyka utrzymywania małego drzewa integracji?
Odpowiedzi:
Możesz zrobić naprzód typedef. Ale do zrobienia
typedef A B;
najpierw musisz przekazać dalej A
:
class A;
typedef A B;
typedef
nazwa złożonego typu szablonu wielopoziomowego przy użyciu deklaracji przekazywania w ten sposób jest dość złożona i trudna. Nie wspominając, że może to wymagać zanurzenia się w szczegóły implementacji ukryte w domyślnych argumentach szablonu. A rozwiązaniem końcowym jest długi i nieczytelny kod (szczególnie gdy typy pochodzą z różnych przestrzeni nazw) bardzo podatny na zmiany w oryginalnym typie.
Dla tych z was jak ja, którzy chcą przekazać deklarację struktury w stylu C, która została zdefiniowana przy użyciu typedef, w jakimś kodzie c ++ znalazłem rozwiązanie, które wygląda następująco ...
// 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;
}
Aby „fwd zadeklarować typedef”, musisz fwd zadeklarować klasę lub strukturę, a następnie możesz wpisać typ zadeklarowany. Kompilator akceptuje wiele identycznych typów czcionek.
długa forma:
class MyClass;
typedef MyClass myclass_t;
skrócona forma:
typedef class MyClass myclass_t;
W C ++ (ale nie w zwykłym C) jest całkowicie legalne dwukrotne wpisanie typu, o ile obie definicje są całkowicie identyczne:
// 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
pola w ten sposób, skoro A
z definicji jest puste?
Ponieważ aby zadeklarować typ, jego rozmiar musi być znany. Możesz przekazać deklarację wskaźnika do typu lub wpisać do wskaźnika typ.
Jeśli naprawdę tego chcesz, możesz użyć idiomu pimpl, aby zmniejszyć liczbę włączeń. Ale jeśli chcesz użyć typu zamiast wskaźnika, kompilator musi znać jego rozmiar.
Edycja: j_random_hacker dodaje ważną kwalifikację do tej odpowiedzi, w zasadzie, że rozmiar musi być znany, aby użyć tego typu, ale deklaracja przekazania może być złożona, jeśli musimy tylko wiedzieć, że typ istnieje , aby utworzyć wskaźniki lub odniesienia do rodzaj. Ponieważ OP nie wyświetlał kodu, ale narzekał, że się nie skompiluje, założyłem (prawdopodobnie poprawnie), że OP próbował użyć tego typu, a nie tylko się do niego odwoływać.
Korzystanie z deklaracji przesyłania zamiast pełnych #include
jest możliwe tylko wtedy, gdy nie zamierzasz używać samego typu (w zakresie tego pliku), ale wskaźnik lub odwołanie do niego.
Aby użyć samego typu, kompilator musi znać jego rozmiar - stąd należy zobaczyć jego pełną deklarację - dlatego #include
potrzebna jest pełna .
Jednak rozmiar wskaźnika lub odwołania jest znany kompilatorowi, niezależnie od wielkości pointee, więc wystarczająca jest deklaracja forward - deklaruje nazwę identyfikatora typu.
Co ciekawe, gdy używasz wskaźnika lub odwołania do class
lub struct
typów, kompilator może obsługiwać typy niekompletne, co pozwala zaoszczędzić czas na przekazywanie deklarowanych typów 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
Miałem ten sam problem, nie chciałem zadzierać z wieloma typedefami w różnych plikach, więc rozwiązałem to z dziedziczeniem:
był:
class BurstBoss {
public:
typedef std::pair<Ogre::ParticleSystem*, bool> ParticleSystem; // removed this with...
zrobił:
class ParticleSystem : public std::pair<Ogre::ParticleSystem*, bool>
{
public:
ParticleSystem(Ogre::ParticleSystem* system, bool enabled) : std::pair<Ogre::ParticleSystem*, bool>(system, enabled) {
};
};
Działa jak urok. Oczywiście musiałem zmienić wszelkie odniesienia
BurstBoss::ParticleSystem
po prostu
ParticleSystem
Zastąpiłem typedef
( using
a konkretnie) dziedziczeniem i konstruktorem (?).
Oryginalny
using CallStack = std::array<StackFrame, MAX_CALLSTACK_DEPTH>;
Zastąpiony
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;
};
W ten sposób mogłem przekazać deklarację za CallStack
pomocą:
class CallStack;
Jak zauważył Bill Kotsias, jedynym rozsądnym sposobem na zachowanie poufności danych typedef twojego punktu i przesłanie ich dalej jest dziedziczenie. Możesz to zrobić nieco lepiej z C ++ 11. Rozważ to:
// 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;
};
Podobnie jak @BillKotsias, użyłem dziedziczenia i zadziałało to dla mnie.
Zmieniłem ten bałagan (który wymagał wszystkich nagłówków boost w mojej deklaracji * .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;
do tej deklaracji (* .h)
class VanillaAccumulator;
std::unique_ptr<VanillaAccumulator> acc;
a implementacja (* .cpp) była
#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
>>
{
};