Comment créer un typedef conditionnel en C ++

89

J'essaye de faire quelque chose comme ça:

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

mais j'obtiens cette erreur:

error: missing binary operator before token "("

Comment puis-je créer correctement le typedef conditionnel?

Martin Drozdik
la source
25
Le préprocesseur ne sait rien sur les sizeofautres constructions C ++. Il ne connaît certainement pas les choses avec typedeflesquelles vous vous êtes créé , car cela n'a même pas encore été analysé.
Courses de légèreté en orbite le
2
Vous pouvez utiliser enable_ifou conditionalpour définir conditionnellement des typedefs, mais vous ne pouvez pas utiliser de préprocesseur pour cela.
Bartek Banachewicz
1
@LightnessRacesinOrbit: Le prétraitement et la compilation sont intégrés dans GCC, il n'est donc pas seulement certain que le code de traitement logiciel ne connaisse pas les définitions de type créées par l'utilisateur, mais il est connu pour être faux dans le cas de GCC. La raison sizeofne peut pas fonctionner dans les conditions d'un préprocesseur est que le langage est défini de cette façon, pas à cause du fonctionnement d'une implémentation.
Eric Postpischil
1
@LightnessRacesinOrbit: Les phases de traduction définissent la syntaxe et la sémantique, pas l'ordre de traitement. Selon C ++ 2011 (N3092) 2.2 [lex.phases] note 11, «Les implémentations doivent se comporter comme si ces phases séparées se produisaient, bien qu'en pratique différentes phases puissent être repliées ensemble.» Mon point sur GCC est pertinent car il démontre que votre affirmation selon laquelle c'est ainsi qu'une implémentation fonctionne est erronée. En d'autres termes, votre commentaire prétend qu'une méthode particulière de mise en œuvre empêche cela. Mais ce n'est pas l'implémentation qui empêche cela (nous pourrions le faire); c'est la définition de la langue.
Eric Postpischil
1
@Eric: Je ne voulais pas revendiquer quoi que ce soit sur les implémentations. Je n'en ai certainement mentionné aucun en particulier. Mon commentaire indiquait un comportement soumis à la règle du «comme si», tout comme le vôtre. Je ne pense pas que nous soyons réellement en désaccord sur quoi que ce soit ici - votre juriste linguistique aurait tout aussi bien pu venir directement du miroir. :)
Courses de légèreté en orbite

Réponses:

139

Utilisez la std::conditionalméta-fonction de C ++ 11.

#include <type_traits>  //include this

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

Notez que si le type que vous utilisez sizeofest un paramètre de modèle, par exemple T, vous devez utiliser typenamecomme:

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

Ou faites Enginedépendre Tcomme:

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

C'est flexible , car vous pouvez maintenant l'utiliser comme:

Engine<int>  engine1;
Engine<long> engine2;
Engine<T>    engine3; // where T could be template parameter!
Nawaz
la source
4
+1 Petite petite bêtise: La vérification sizeof(int) <= 4n'est peut-être pas un moyen très portable puisque sur une machine Windows 64 bits, le compilateur GCC (MinGW) x64 donne sizeof(int) = sizeof(long) = 4. Une meilleure façon serait sizeof(void*) <= 4.
legends2k
@ legends2k: Vous voulez dire Engine<void*> engine4;? ;-)
Nawaz
2
@Nawaz: Bien sûr que non :) Je voulais dire std::conditional<sizeof(void*) <= 4, std::mt19937, std::mt19937_64>dans le premier extrait de code.
legends2k
1
@ legends2k: Pourquoi utiliseriez-vous cela si je vous en ai fourni Engine<void*>? : P
Nawaz
@Nawaz: Haha ... c'est vrai. Cependant, je pensais que l'OP devrait probablement connaître le piège de la détection de l'architecture en fonction de la taille d'un int:)
legends2k
35

En utilisant, std::conditionalvous pouvez le faire comme ceci:

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

Si vous voulez faire un typedef, vous pouvez le faire aussi.

typedef std::conditional<sizeof(int) <= 4, 
                         std::mt19937, 
                         std::mt19937_64
                         >::type Engine
Rapptz
la source
Il n'y a pas besoin d' typenameici
gx_
@gx_ Ouais, l'habitude de le mettre là-bas en travaillant avec des modèles, pas des types concrets.
Rapptz
1
@LightnessRacesinOrbit Eh bien, je l'ai corrigé un peu.
Rapptz
5

Si vous n'avez pas C ++ 11 disponible (même si cela semble être le cas si vous prévoyez de l'utiliser std::mt19937), vous pouvez implémenter la même chose sans le support de C ++ 11 en utilisant la bibliothèque de métaprogrammation Boost (MPL) . Voici un exemple compilable:

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

Cela imprime le nom mutilé de foosur mon système, car il intfait 4 octets ici.

Jason R
la source
1
Pourquoi n'utilisez-vous pas à la if_cplace? Il serait plus facile d'écrire doit (et comprendre): mpl::if_c<sizeof(int)<=4, foo, bar>::type. N'est-ce pas?
Nawaz
1
@Nawaz: En effet, c'est mieux à bien des égards. J'avais oublié mpl::if_c. J'ai mis à jour l'exemple pour utiliser cette approche à la place.
Jason R