Jak utworzyć warunkową definicję typu w C ++


90

Próbuję zrobić coś takiego:

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

ale otrzymuję ten błąd:

error: missing binary operator before token "("

Jak mogę poprawnie utworzyć warunkową czcionkę?


25
Preprocesor nic nie wie o sizeofani o innych konstrukcjach C ++. Z pewnością nie wie o rzeczach, z których sam stworzyłeś typedef, ponieważ nie zostało to jeszcze przeanalizowane.
Wyścigi lekkości na orbicie,

2
Możesz użyć enable_iflub, conditionalaby warunkowo zdefiniować typedef, ale nie możesz użyć do tego preprocesora.
Bartek Banachewicz

1
@LightnessRacesinOrbit: Wstępne przetwarzanie i kompilacja są zintegrowane z GCC, więc nie tylko nie jest pewne, że kod przetwarzający oprogramowanie nie wie o definicjach typów utworzonych przez użytkownika, ale wiadomo, że jest fałszywy w przypadku GCC. Przyczyną sizeofniemożności działania w warunkach preprocesora jest to, że język jest zdefiniowany w ten sposób, a nie sposób działania implementacji.
Eric Postpischil

1
@LightnessRacesinOrbit: Fazy tłumaczenia definiują składnię i semantykę, a nie kolejność przetwarzania. Według C ++ 2011 (N3092) 2.2 [lex.phases] przypis 11: „Implementacje muszą zachowywać się tak, jakby miały miejsce te oddzielne fazy, chociaż w praktyce różne fazy mogą być złożone razem”. Moja uwaga dotycząca GCC jest istotna, ponieważ pokazuje, że twoje twierdzenie, że w ten sposób działa implementacja, jest błędne. Innymi słowy, Twój komentarz twierdzi, że zapobiega temu określona metoda implementacji. Ale to nie implementacja zapobiega temu (moglibyśmy to zrobić); to jest definicja języka.
Eric Postpischil

1
@Eric: Nie chciałem w ogóle twierdzić, że chodzi o implementacje. Z pewnością nie wymieniłem żadnego konkretnego. Mój komentarz wskazywał na zachowanie, które podlega zasadzie as-if, tak samo jak twoje. Nie sądzę, żebyśmy się co do niczego tutaj nie zgadzali - prawnik z twojego języka równie dobrze mógł wyjść prosto z lustra. :)
Wyścigi lekkości na orbicie

Odpowiedzi:


139

Użyj std::conditionalmeta-funkcji z C ++ 11.

#include <type_traits>  //include this

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

Zauważ, że jeśli typ, którego używasz w, sizeofjest, powiedzmy T, parametrem szablonu , musisz użyć typenamejako:

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

Lub Engineuzależnij się Tod:

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

To jest elastyczne , ponieważ teraz możesz go używać jako:

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

4
+1 Mały chwytak: sprawdzanie sizeof(int) <= 4może nie jest zbyt przenośnym sposobem, ponieważ na 64-bitowej maszynie z systemem Windows daje kompilator GCC (MinGW) x64 sizeof(int) = sizeof(long) = 4. Byłby lepszy sposób sizeof(void*) <= 4.
legends2k

@ legends2k: Masz na myśli Engine<void*> engine4;? ;-)
Nawaz

2
@Nawaz: Oczywiście, że nie :) Miałem na myśli std::conditional<sizeof(void*) <= 4, std::mt19937, std::mt19937_64>w pierwszym fragmencie kodu.
legends2k

1
@ legends2k: Dlaczego miałbyś tego używać, skoro ci to zapewniłem Engine<void*>? : P
Nawaz

@Nawaz: Haha ... to prawda. Jednak pomyślałem, że OP powinien prawdopodobnie znać pułapkę w wykrywaniu architektury na podstawie rozmiaru int:)
legends2k

35

Używając std::conditionalmożesz to zrobić tak:

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

Jeśli chcesz zrobić typedef, możesz to zrobić.

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

Nie ma potrzeby typenametutaj
gx_

@gx_ Tak, zwykłem umieszczać to tam podczas pracy z szablonami, a nie konkretnymi typami.
Rapptz,

1
@LightnessRacesinOrbit Cóż, trochę to naprawiłem.
Rapptz,

5

Jeśli nie masz dostępnego C ++ 11 (chociaż wydaje się, że tak, jeśli planujesz używać std::mt19937), możesz zaimplementować to samo bez obsługi C ++ 11 za pomocą biblioteki Boost Metaprogramming Library (MPL) . Oto przykład do kompilacji:

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

Spowoduje to wyświetlenie zniekształconej nazwy foow moim systemie, ponieważ inttutaj jest 4 bajty.


1
Dlaczego nie używasz if_czamiast tego? Byłoby koniecznością łatwiej napisać (i zrozumieć) mpl::if_c<sizeof(int)<=4, foo, bar>::type. Prawda?
Nawaz,

1
@Nawaz: Rzeczywiście, jest to lepsze pod wieloma względami. Zapomniałem o mpl::if_c. Zaktualizowałem przykład, aby zamiast tego użyć tego podejścia.
Jason R,
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.