So erstellen Sie ein bedingtes typedef in C ++

89

Ich versuche so etwas zu tun:

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

aber ich bekomme diesen Fehler:

error: missing binary operator before token "("

Wie kann ich das bedingte typedef korrekt erstellen?

Martin Drozdik
quelle
25
Der Präprozessor weiß nichts über sizeofoder andere C ++ - Konstrukte. Es weiß sicherlich nichts über Dinge, mit denen Sie sich selbst erstellt haben typedef, da dies noch nicht einmal analysiert wurde.
Leichtigkeitsrennen im Orbit
2
Sie können typedefs verwenden enable_ifoder conditionalbedingt definieren, aber Sie können dafür keinen Präprozessor verwenden.
Bartek Banachewicz
1
@LightnessRacesinOrbit: Vorverarbeitung und Kompilierung sind in GCC integriert, sodass nicht nur nicht sicher ist, ob der Softwareverarbeitungscode keine vom Benutzer erstellten Typdefinitionen kennt, sondern im Fall von GCC als falsch bekannt ist. Der Grund, warum sizeofunter Präprozessorbedingungen nicht funktionieren kann, liegt darin, dass die Sprache auf diese Weise definiert ist und nicht daran, wie eine Implementierung funktioniert.
Eric Postpischil
1
@LightnessRacesinOrbit: Die Übersetzungsphasen definieren Syntax und Semantik, nicht die Reihenfolge der Verarbeitung. Gemäß C ++ 2011 (N3092) 2.2 [lex.phases], Anmerkung 11, „müssen sich Implementierungen so verhalten, als ob diese getrennten Phasen auftreten, obwohl in der Praxis verschiedene Phasen möglicherweise zusammengelegt werden.“ Mein Punkt zu GCC ist relevant, weil er zeigt, dass Ihre Behauptung, dass eine Implementierung so funktioniert, falsch ist. Mit anderen Worten, Ihr Kommentar behauptet, dass eine bestimmte Implementierungsmethode dies verhindert. Aber es ist nicht die Implementierung, die dies verhindert (wir könnten es tun); es ist die Sprachdefinition.
Eric Postpischil
1
@ Eric: Ich wollte überhaupt nichts über Implementierungen behaupten. Ich habe sicherlich keinen bestimmten erwähnt. In meinem Kommentar wurde ein Verhalten angegeben, das ebenso wie Ihres der Als-ob-Regel unterliegt. Ich glaube nicht, dass wir uns hier in irgendetwas nicht einig sind - Ihre Sprachanwaltschaft hätte genauso gut direkt aus dem Spiegel kommen können. :)
Leichtigkeitsrennen im Orbit

Antworten:

138

Verwenden Sie die std::conditionalMetafunktion aus C ++ 11.

#include <type_traits>  //include this

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

Beachten Sie, dass Sie, wenn der Typ, in dem Sie verwenden, sizeofbeispielsweise ein Vorlagenparameter ist, Folgendes Tverwenden müssen typename:

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

Oder machen Sie Engineabhängig von T:

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

Das ist flexibel , denn jetzt können Sie es verwenden als:

Engine<int>  engine1;
Engine<long> engine2;
Engine<T>    engine3; // where T could be template parameter!
Nawaz
quelle
4
+1 Minor nitpick: Das Überprüfen auf sizeof(int) <= 4ist möglicherweise keine sehr portable Methode, da auf einem 64-Bit-Windows-Computer der GCC (MinGW) x64-Compiler dies ermöglicht sizeof(int) = sizeof(long) = 4. Ein besserer Weg wäre sizeof(void*) <= 4.
Legends2k
@ legends2k: Du meinst Engine<void*> engine4;? ;-)
Nawaz
2
@Nawaz: Natürlich nicht :) Ich meinte std::conditional<sizeof(void*) <= 4, std::mt19937, std::mt19937_64>im ersten Code-Snippet.
Legends2k
1
@ legends2k: Warum würdest du das benutzen, wenn ich dir etwas zur Verfügung gestellt habe Engine<void*>? : P
Nawaz
@Nawaz: Haha ... das stimmt. Ich dachte jedoch, dass das OP wahrscheinlich die Gefahr kennen sollte, die Architektur basierend auf der Größe eines int:)
legends2k
35

Mit std::conditionalkönnen Sie es so machen:

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

Wenn Sie eine machen wollen typedef, können Sie das auch machen.

typedef std::conditional<sizeof(int) <= 4, 
                         std::mt19937, 
                         std::mt19937_64
                         >::type Engine
Rapptz
quelle
Es gibt keine Notwendigkeit für typenamehier
gx_
@gx_ Ja, ich bin es gewohnt, es dort zu platzieren, weil ich mit Vorlagen gearbeitet habe, nicht mit konkreten Typen.
Rapptz
1
@ LightnessRacesinOrbit Nun, ich habe es ein wenig behoben.
Rapptz
5

Wenn Sie nicht über C ++ 11 verfügen (obwohl dies anscheinend der Fall ist, wenn Sie dies planen std::mt19937), können Sie dasselbe mithilfe der Boost Metaprogramming Library (MPL) ohne C ++ 11-Unterstützung implementieren . Hier ist ein kompilierbares Beispiel:

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

Dies gibt den verstümmelten Namen von fooauf meinem System aus, da inthier 4 Bytes sind.

Jason R.
quelle
1
Warum benutzt du nicht if_cstattdessen? Es wäre einfacher zu schreiben (und zu verstehen) : mpl::if_c<sizeof(int)<=4, foo, bar>::type. Ist es nicht?
Nawaz
1
@Nawaz: In der Tat ist das in vielerlei Hinsicht besser. Ich hatte es vergessen mpl::if_c. Ich habe das Beispiel aktualisiert, um stattdessen diesen Ansatz zu verwenden.
Jason R