Cara membuat typedef bersyarat di C ++

90

Saya mencoba melakukan sesuatu seperti ini:

#include <iostream>
#include <random>

typedef int Integer;

#if sizeof(Integer) <= 4
    typedef std::mt19937     Engine;
#else
    typedef std::mt19937_64  Engine;
#endif

int main()
{
    std::cout << sizeof(Integer) << std::endl;
    return 0;
}

tapi saya mendapatkan kesalahan ini:

error: missing binary operator before token "("

Bagaimana saya bisa membuat typedef bersyarat dengan benar?

Martin Drozdik
sumber
25
Preprocessor tidak tahu apa-apa tentang sizeof, atau konstruksi C ++ lainnya. Itu pasti tidak tahu tentang hal-hal yang Anda buat sendiri typedef, karena itu bahkan belum diuraikan.
Balapan Ringan di Orbit
2
Anda dapat menggunakan enable_ifatau conditionaluntuk mendefinisikan typedef secara bersyarat, tetapi Anda tidak dapat menggunakan preprocessor untuk itu.
Bartek Banachewicz
1
@LightnessRacesinOrbit: Pemrosesan awal dan kompilasi terintegrasi dalam GCC, jadi tidak hanya tidak pasti bahwa kode pemrosesan perangkat lunak tidak mengetahui tentang definisi jenis yang dibuat pengguna tetapi juga diketahui salah dalam kasus GCC. Alasan sizeoftidak dapat bekerja dalam kondisi preprocessor adalah karena bahasanya didefinisikan seperti itu, bukan karena cara kerja implementasi.
Eric Postpischil
1
@LightnessRacesinOrbit: Fase terjemahan menentukan sintaks dan semantik, bukan urutan pemrosesan. Per C ++ 2011 (N3092) 2.2 [lex.phases] catatan 11, “Implementasi harus berperilaku seolah-olah fase terpisah ini terjadi, meskipun dalam praktiknya fase yang berbeda mungkin digabungkan menjadi satu.” Poin saya tentang GCC relevan karena ini menunjukkan bahwa klaim Anda bahwa ini adalah cara kerja penerapan adalah salah. Dengan kata lain, komentar Anda mengklaim bahwa metode penerapan tertentu mencegah hal ini. Tetapi bukan implementasi yang mencegah hal ini (kami bisa melakukannya); itu adalah definisi bahasa.
Eric Postpischil
1
@ Eric: Saya tidak bermaksud untuk mengklaim apa pun tentang implementasi apa pun. Saya pasti tidak menyebutkan satu pun. Komentar saya menyatakan perilaku yang tunduk pada aturan seolah-olah, seperti halnya perilaku Anda. Saya tidak berpikir kami sebenarnya tidak setuju pada apa pun di sini - pengacara bahasa Anda mungkin juga datang langsung dari cermin. :)
Balapan Lightness di Orbit

Jawaban:

139

Gunakan std::conditionalmeta-function dari C ++ 11.

#include <type_traits>  //include this

typedef std::conditional<sizeof(int) <= 4,
                         std::mt19937,
                         std::mt19937_64>::type Engine;

Perhatikan bahwa jika tipe yang Anda gunakan sizeofadalah parameter template, katakanlah T, Anda harus menggunakan typenamesebagai:

typedef typename std::conditional<sizeof(T) <= 4, // T is template parameter
                                  std::mt19937,
                                  std::mt19937_64>::type Engine;

Atau Enginebergantung pada T:

template<typename T>
using Engine = typename std::conditional<sizeof(T) <= 4, 
                                         std::mt19937,
                                         std::mt19937_64>::type;

Itu fleksibel , karena sekarang Anda bisa menggunakannya sebagai:

Engine<int>  engine1;
Engine<long> engine2;
Engine<T>    engine3; // where T could be template parameter!
Nawaz
sumber
4
+1 Nitpick kecil: Memeriksa sizeof(int) <= 4mungkin bukan cara yang sangat portabel karena pada mesin Windows 64-bit, kompiler GCC (MinGW) x64 memberikan sizeof(int) = sizeof(long) = 4. Cara yang lebih baik adalah sizeof(void*) <= 4.
legends2k
@ legends2k: Maksud Anda Engine<void*> engine4;? ;-)
Nawaz
2
@Nawaz: Tentu saja tidak :) yang saya maksud std::conditional<sizeof(void*) <= 4, std::mt19937, std::mt19937_64>di potongan kode pertama.
legends2k
1
@ legends2k: Mengapa Anda menggunakan itu jika saya telah memberi Anda Engine<void*>? : P
Nawaz
@Nawaz: Haha ... itu benar. Namun, saya pikir OP mungkin harus mengetahui kesulitan dalam mendeteksi arsitektur berdasarkan ukuran int:)
legends2k
35

Menggunakan std::conditionalAnda dapat melakukannya seperti ini:

using Engine = std::conditional<sizeof(int) <= 4, 
                               std::mt19937, 
                               std::mt19937_64
                               >::type;

Jika Anda ingin melakukan typedef, Anda juga dapat melakukannya.

typedef std::conditional<sizeof(int) <= 4, 
                         std::mt19937, 
                         std::mt19937_64
                         >::type Engine
Rapptz
sumber
Tidak perlu di typenamesini
gx_
@gx_ Ya, biasanya meletakkannya di sana dari bekerja dengan template, bukan tipe konkret.
Rapptz
1
@LightnessRacesinOrbit Ya, saya memperbaikinya sedikit.
Rapptz
5

Jika Anda tidak memiliki C ++ 11 yang tersedia (meskipun tampaknya Anda melakukannya jika Anda berencana untuk menggunakan std::mt19937), maka Anda dapat mengimplementasikan hal yang sama tanpa dukungan C ++ 11 menggunakan Boost Metaprogramming Library (MPL) . Berikut adalah contoh kompilasi:

#include <boost/mpl/if.hpp>
#include <iostream>
#include <typeinfo>

namespace mpl = boost::mpl;

struct foo { };
struct bar { };

int main()
{
    typedef mpl::if_c<sizeof(int) <= 4, foo, bar>::type Engine;

    Engine a;
    std::cout << typeid(a).name() << std::endl;
}

Ini mencetak nama yang rusak foodi sistem saya, sebagai int4 byte di sini.

Jason R
sumber
1
Mengapa Anda tidak menggunakan if_csaja? Ini akan menjadi keharusan mudah untuk menulis (dan memahami): mpl::if_c<sizeof(int)<=4, foo, bar>::type. Bukan?
Nawaz
1
@Nawaz: Memang, itu lebih baik dalam beberapa hal. Saya sudah lupa mpl::if_c. Saya memperbarui contoh untuk menggunakan pendekatan itu sebagai gantinya.
Jason R