Come creare un typedef condizionale in C ++


89

Sto cercando di fare qualcosa del genere:

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

ma ottengo questo errore:

error: missing binary operator before token "("

Come posso rendere correttamente il typedef condizionale?


25
Il preprocessore non sa nulla sizeofo altri costrutti C ++. E di certo non sa di cose che voi stessi creati con typedef, come quella è nemmeno stato ancora analizzato.
Gare di leggerezza in orbita il

2
Potresti usare enable_ifo conditionalper definire in modo condizionale i typedef, ma non puoi usare il preprocessore per quello.
Bartek Banachewicz

1
@LightnessRacesinOrbit: la preelaborazione e la compilazione sono integrate in GCC, quindi non solo non è certo che il codice di elaborazione del software non conosca le definizioni di tipo create dall'utente, ma è noto che è falso nel caso di GCC. Il motivo sizeofnon può funzionare in condizioni di un preprocessore è perché il linguaggio è definito in questo modo, non a causa di come funziona un'implementazione.
Eric Postpischil

1
@LightnessRacesinOrbit: le fasi di traduzione definiscono la sintassi e la semantica, non l'ordine di elaborazione. Per C ++ 2011 (N3092) 2.2 [lex.phases] nota 11, "Le implementazioni devono comportarsi come se si verificassero queste fasi separate, sebbene in pratica le diverse fasi potrebbero essere raggruppate insieme." Il mio punto di vista su GCC è rilevante perché dimostra che la tua affermazione che questo è il modo in cui funziona un'implementazione è sbagliata. In altre parole, il tuo commento afferma che un particolare metodo di implementazione lo impedisce. Ma non è l'implementazione che impedisce questo (abbiamo potuto farlo); è la definizione della lingua.
Eric Postpischil

1
@ Eric: non intendevo affermare nulla sulle implementazioni di sorta. Di certo non ne ho menzionato uno in particolare. Il mio commento dichiarava un comportamento soggetto alla regola del come se, proprio come il tuo. Non credo che siamo effettivamente in disaccordo su nulla qui - la tua avvocatura linguistica potrebbe anche essere venuta direttamente dallo specchio. :)
Gare di leggerezza in orbita il

Risposte:


139

Usa la std::conditionalmeta-funzione di C ++ 11.

#include <type_traits>  //include this

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

Nota che se il tipo che usi sizeofè un parametro del modello, ad esempio T, devi usare typenamecome:

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

Oppure fai Engineaffidamento su Tcome:

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

È flessibile , perché ora puoi usarlo come:

Engine<int>  engine1;
Engine<long> engine2;
Engine<T>    engine3; // where T could be template parameter!

4
+1 Minor nitpick: il controllo sizeof(int) <= 4non è forse un modo molto portabile poiché su una macchina Windows a 64 bit, il compilatore GCC (MinGW) x64 dà sizeof(int) = sizeof(long) = 4. Un modo migliore sarebbe sizeof(void*) <= 4.
legends2k

@ legends2k: Vuoi dire Engine<void*> engine4;? ;-)
Nawaz

2
@Nawaz: Ovviamente no :) Intendevo std::conditional<sizeof(void*) <= 4, std::mt19937, std::mt19937_64>nel primo frammento di codice.
legends2k

1
@ legends2k: Perché dovresti usarlo se te l'ho fornito Engine<void*>? : P
Nawaz

@Nawaz: Haha ... è vero. Tuttavia, ho pensato che l'OP dovrebbe probabilmente conoscere la trappola nel rilevare l'architettura in base alle dimensioni di un int:)
legends2k

35

Usandolo std::conditionalpuoi farlo in questo modo:

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

Se vuoi fare un typedef, puoi farlo anche tu.

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

Non c'è bisogno di typenamequi
gx_

@gx_ Sì, ero abituato a metterlo lì lavorando con modelli, non tipi concreti.
Rapptz

1
@LightnessRacesinOrbit Beh, l'ho aggiustato un po '.
Rapptz

5

Se non hai C ++ 11 disponibile (anche se sembra che tu lo faccia se hai intenzione di usarlo std::mt19937), puoi implementare la stessa cosa senza il supporto di C ++ 11 usando la Boost Metaprogramming Library (MPL) . Ecco un esempio compilabile:

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

Questo stampa il nome alterato di foosul mio sistema, poiché intqui è di 4 byte.


1
Perché non usi if_cinvece? Sarebbe mosto più facile da scrivere (e capire): mpl::if_c<sizeof(int)<=4, foo, bar>::type. Non è vero?
Nawaz

1
@Nawaz: In effetti, è meglio in molti modi. Me ne ero dimenticato mpl::if_c. Ho aggiornato l'esempio per utilizzare invece quell'approccio.
Jason R
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.