Teruskan deklarasi typedef di C ++

235

Mengapa kompiler tidak membiarkan saya meneruskan menyatakan typedef?

Dengan asumsi itu tidak mungkin, apa praktik terbaik untuk menjaga pohon inklusi saya tetap kecil?

pengguna96825
sumber

Jawaban:

170

Anda dapat melakukan forward typedef. Tapi untuk dilakukan

typedef A B;

Anda harus terlebih dahulu menyatakan A:

class A;

typedef A B;
Hong Jiang
sumber
11
Memberi +1 pada akhirnya karena meskipun secara teknis Anda tidak dapat "mem-forward-typedef" (yaitu Anda tidak bisa menulis "typedef A;"), Anda hampir dapat mencapai apa yang ingin dicapai OP menggunakan trik Anda di atas.
j_random_hacker
9
Tetapi berhati-hatilah, jika typedef berubah, Anda juga dapat mengubah semua deklarasi maju tersebut, yang mungkin Anda lewatkan jika typedef lama dan baru menggunakan tipe dengan antarmuka yang sama.
matematika
50
Secara umum ini bukan solusi yang berguna. Misalnya jika typedefnama tipe templat bertingkat kompleks menggunakan deklarasi maju cara ini agak rumit dan sulit. Belum lagi bahwa itu mungkin memerlukan menyelam ke detail implementasi yang disembunyikan dalam argumen template default. Dan solusi akhirnya adalah kode yang panjang dan tidak dapat dibaca (terutama ketika jenis berasal dari berbagai ruang nama) sangat rentan terhadap perubahan dalam jenis aslinya.
Adam Badura
3
Juga ini menunjukkan "detail implementasi" (bahkan jika tidak sepenuhnya tetapi masih ...) sementara ide di belakang deklarasi adalah untuk menyembunyikannya.
Adam Badura
3
@ jendela jendela: Itu: template <class T> class A; mengetikkan A <C> B;
milianw
47

Bagi Anda seperti saya, yang ingin maju mendeklarasikan struct C-style yang didefinisikan menggunakan typedef, dalam beberapa kode c ++, saya telah menemukan solusi yang berjalan sebagai berikut ...

// 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;
 }
Little John
sumber
4
@LittleJohn Masalah dengan solusi ini adalah bahwa dummy-name _bah tidak dianggap sebagai bagian dari API publik. Lihat maju FILE delcare.
user877329
23

Untuk "fwd mendeklarasikan typedef" Anda perlu fwd mendeklarasikan kelas atau struct dan kemudian Anda dapat mengetikkan typeed yang dideklarasikan. Beberapa typedef identik dapat diterima oleh kompiler.

bentuk panjang:

class MyClass;
typedef MyClass myclass_t;

bentuk pendek:

typedef class MyClass myclass_t;
Pavel P
sumber
Bagaimana ini berbeda dari pertanyaan yang paling banyak dipilih? stackoverflow.com/a/804956/931303
Jorge Leitao
1
@ JorgeLeitão Anda tidak melihat perbedaannya? Itu tidak menunjukkan bagaimana melakukan itu dalam satu baris.
Pavel P
17

Di C ++ (tapi bukan C biasa), sangat sah untuk mengetikkan tipe dua kali, asalkan kedua definisi benar - benar identik:

// 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
sumber
34
Pemeliharaan Tidak Tidak. Hal seperti ini akan menggigit Anda di keister cepat atau lambat.
Mark Storer
3
@ Markarktor, setidaknya kompiler akan menangkap perbedaan dan menghasilkan kesalahan. Saya memverifikasi ini dengan Visual C ++.
Alan
Bagus, tetapi bagaimana Anda mendefinisikan Abidang dengan cara ini karena Akosong menurut definisi?
Patrizio Bertoni
10

Karena untuk mendeklarasikan suatu tipe, ukurannya perlu diketahui. Anda dapat meneruskan mendeklarasikan pointer ke tipe, atau mengetikkan pointer ke tipe.

Jika Anda benar-benar ingin, Anda dapat menggunakan idiom pimpl untuk menjaga agar menyertakan. Tetapi jika Anda ingin menggunakan tipe, daripada pointer, kompiler harus mengetahui ukurannya.

Sunting: j_random_hacker menambahkan kualifikasi penting untuk jawaban ini, pada dasarnya ukuran itu perlu diketahui untuk menggunakan tipe tersebut, tetapi deklarasi maju dapat dibuat jika kita hanya perlu mengetahui tipe yang ada , untuk membuat pointer atau referensi ke Tipe. Karena OP tidak menunjukkan kode, tetapi mengeluh itu tidak dapat dikompilasi, saya berasumsi (mungkin dengan benar) bahwa OP sedang mencoba menggunakan tipe tersebut, tidak hanya merujuknya.

tpdi
sumber
35
Yah, forward deklarasi tipe-tipe kelas mendeklarasikan tipe-tipe ini tanpa sepengetahuan ukurannya. Selain itu, selain dapat mendefinisikan pointer dan referensi ke tipe yang tidak lengkap tersebut, fungsi dapat dideklarasikan (tetapi tidak didefinisikan) yang mengambil parameter dan / atau mengembalikan nilai dari tipe tersebut.
j_random_hacker
3
Maaf saya tidak berpikir itu asumsi yang bagus. Jawaban ini tidak penting. Ini sangat banyak kasus pengetikan deklarasi maju.
Kue
6

Menggunakan penerusan deklarasi alih - alih#include s penuh hanya dimungkinkan bila Anda tidak bermaksud menggunakan tipe itu sendiri (dalam cakupan file ini) tetapi sebuah pointer atau referensi untuk itu.

Untuk menggunakan tipe itu sendiri, kompiler harus mengetahui ukurannya - maka deklarasi lengkapnya harus dilihat - maka diperlukan penuh #include.

Namun, ukuran pointer atau referensi diketahui oleh kompiler, terlepas dari ukuran pointee, sehingga pernyataan maju sudah cukup - itu menyatakan nama pengenal jenis.

Menariknya, ketika menggunakan pointer atau referensi classatau structtipe, kompiler dapat menangani tipe yang tidak lengkap sehingga Anda tidak perlu meneruskan menyatakan tipe pointee juga:

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

Saya memiliki masalah yang sama, tidak ingin dipusingkan dengan beberapa typedef dalam file yang berbeda, jadi saya menyelesaikannya dengan warisan:

dulu:

class BurstBoss {

public:

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

melakukan:

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

public:

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

Bekerja seperti pesona. Tentu saja, saya harus mengubah referensi apa pun dari

BurstBoss::ParticleSystem

untuk sederhana

ParticleSystem
RUU Kotsias
sumber
1

Saya mengganti typedef( usinglebih spesifik) dengan warisan dan konstruktor (?).

Asli

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

Diganti

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

Dengan cara ini saya bisa maju menyatakan CallStackdengan:

class CallStack;
Tidak dalam daftar
sumber
0

Seperti yang dicatat oleh Bill Kotsias, satu-satunya cara yang masuk akal untuk menjaga detail typedef poin Anda tetap pribadi, dan meneruskannya adalah dengan warisan. Anda dapat melakukannya sedikit lebih baik dengan C ++ 11 sekalipun. Pertimbangkan ini:

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

Seperti @BillKotsias, saya menggunakan warisan, dan itu berhasil untuk saya.

Saya mengubah kekacauan ini (yang mengharuskan semua tajuk boost dalam deklarasi saya * .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;

dalam deklarasi ini (* .h)

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

dan implementasinya (* .cpp) tadinya

#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
sumber