Przekazywanie deklaracji typu typedef w C ++

235

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?

użytkownik96825
źródło

Odpowiedzi:

170

Możesz zrobić naprzód typedef. Ale do zrobienia

typedef A B;

najpierw musisz przekazać dalej A:

class A;

typedef A B;
Hong Jiang
źródło
11
+1 na końcu, ponieważ chociaż technicznie nie możesz napisać „naprzód typedef” (tzn. Nie możesz napisać „typedef A;”), prawie na pewno możesz osiągnąć to, co OP chce osiągnąć, używając powyższej sztuczki.
j_random_hacker
9
Ale pamiętaj, że jeśli typedef się zmieni, możesz również zmienić te wszystkie deklaracje przekazywania, których możesz przegapić, jeśli stary i nowy typedef używają typów z tym samym interfejsem.
matematyka
50
Zasadniczo nie jest to przydatne rozwiązanie. Na przykład, jeśli typedefnazwa 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.
Adam Badura
3
Pokazuje to także „szczegóły implementacji” (nawet jeśli nie do końca, ale nadal…), podczas gdy deklaracja forward polegała na ich ukryciu.
Adam Badura
3
@windfinder: Robi: szablon <klasa T> klasa A; typedef A <C> B;
milianw
47

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;
 }
Mały John
źródło
4
@LittleJohn Problem z tym rozwiązaniem polega na tym, że nazwa manekina _bah nie jest uważana za część publicznego API. Zobacz dalej plik DELcare.
user877329,
23

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;
Pavel P.
źródło
Czym różni się to od najczęściej głosowanego pytania? stackoverflow.com/a/804956/931303
Jorge Leitao
1
@ JorgeLeitão nie widzisz, jak to jest inaczej? Nie pokazuje, jak to zrobić w jednym wierszu.
Pavel P
17

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);
Adam Rosenfield
źródło
34
Konserwacja Nie Nie. Tego rodzaju gryzie cię prędzej czy później.
Mark Storer,
3
@ MarkStorer, przynajmniej kompilator wyłapie każdą różnicę i wygeneruje błąd. Zweryfikowałem to za pomocą Visual C ++.
Alan
Fajnie, ale jak definiujesz Apola w ten sposób, skoro Az definicji jest puste?
Patrizio Bertoni
10

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ć.

tpdi
źródło
35
Cóż, deklaracje typu klas deklarują te typy bez znajomości ich wielkości. Ponadto, oprócz możliwości definiowania wskaźników i odniesień do takich niekompletnych typów, można zadeklarować (ale nie zdefiniować) funkcje, które pobierają parametry i / lub zwracają wartość takich typów.
j_random_hacker
3
Niestety nie sądzę, że to dobre założenie. Ta odpowiedź nie ma znaczenia. Jest to w dużej mierze przypadek wpisania deklaracji forward.
Cookie
6

Korzystanie z deklaracji przesyłania zamiast pełnych #includejest 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 #includepotrzebna 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 classlub structtypó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 
Adi Shavit
źródło
2

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
Bill Kotsias
źródło
1

Zastąpiłem typedef( usinga 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 CallStackpomocą:

class CallStack;
Spoza listy
źródło
0

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;
};
Timmmm
źródło
0

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
>>
{
};
Mark Lakata
źródło