Як зробити умовний typedef у C ++


89

Я намагаюся зробити щось подібне:

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

але я отримую цю помилку:

error: missing binary operator before token "("

Як я можу правильно зробити умовний typedef?


25
Препроцесор нічого не знає про sizeofінші конструкції C ++. Він точно не знає про речі, з якими ви створили себе typedef, оскільки це ще навіть не проаналізовано.
Гонки легкості на орбіті

2
Ви можете використовувати enable_ifабо conditionalумовно визначити typedefs, але ви не можете використовувати для цього препроцесор.
Bartek Banachewicz

1
@LightnessRacesinOrbit: попередня обробка та компіляція інтегровані в GCC, тому не тільки не впевнено, що код програмної обробки не знає про визначення типу, створеного користувачем, але, як відомо, є хибним у випадку GCC. Причина sizeofне може працювати в умовах попереднього процесора, оскільки мова визначена таким чином, а не через те, як працює реалізація.
Eric Postpischil

1
@LightnessRacesinOrbit: Фази перекладу визначають синтаксис та семантику, а не порядок обробки. Відповідно до C ++ 2011 (N3092) 2.2 [лекс.фази], примітка 11, “Впровадження повинно вести себе так, ніби ці окремі фази мають місце, хоча на практиці різні фази можуть бути складені разом”. Моя думка щодо GCC актуальна, оскільки вона демонструє, що ваша заява про те, що саме так працює реалізація, є неправильною. Іншими словами, ваш коментар стверджує, що певний метод реалізації запобігає цьому. Але не реалізація перешкоджає цьому (ми могли б це зробити); це визначення мови.
Eric Postpischil

1
@Eric: Я не хотів заявляти нічого про реалізації. Я точно не згадав жодного конкретного. У моєму коментарі зазначено поведінку, яка підпорядковується правилу як би, як і ваша. Я не думаю, що ми насправді тут ні про що не погоджуємось - ваше мовне правопорушення могло б також вийти прямо з дзеркала. :)
Гонки легкості на Орбіті

Відповіді:


139

Використовуйте std::conditionalмета-функцію з C ++ 11.

#include <type_traits>  //include this

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

Зверніть увагу, що якщо тип, який ви використовуєте, sizeofє параметром шаблону, скажімо T, тоді вам доведеться використовувати typenameяк:

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

Або Engineзалежать від Tяк:

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

Це гнучко , оскільки тепер ви можете використовувати його як:

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

4
+1 Незначний шум: Перевірка на наявність sizeof(int) <= 4, можливо, не дуже портативний спосіб, оскільки на 64-розрядної машині Windows дає компілятор GCC (MinGW) x64 sizeof(int) = sizeof(long) = 4. Кращий спосіб був би sizeof(void*) <= 4.
legends2k

@ legends2k: Ви маєте на увазі Engine<void*> engine4;? ;-)
Наваз

2
@Nawaz: Звичайно, ні :) Я мав std::conditional<sizeof(void*) <= 4, std::mt19937, std::mt19937_64>на увазі в першому фрагменті коду.
legends2k

1
@ legends2k: Чому ви могли б це використовувати, якщо я вам це надав Engine<void*>? : P
Nawaz

@Nawaz: Ха-ха ... це правда. Однак я думав, що ОП, мабуть, повинен знати int
підводну камеру

35

За допомогою std::conditionalви можете зробити це так:

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

Якщо ви хочете зробити typedef, ви можете зробити це теж.

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

Там немає необхідності typenameтут
gx_

@gx_ Так, звик ставити його там, працюючи з шаблонами, а не з конкретними типами.
Rapptz

1
@LightnessRacesinOrbit Ну, я це трохи виправив.
Rapptz

5

Якщо у вас немає C ++ 11 (хоча, здається, ви це робите, якщо плануєте використовувати std::mt19937), ви можете реалізувати те саме без підтримки C ++ 11 за допомогою бібліотеки Boost Metaprogramming Library (MPL) . Ось компілювальний приклад:

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

Це друкує спотворене ім'я fooна моїй системі, оскільки тут intє 4 байти.


1
Чому ви не використовуєте if_cзамість цього? Було б необхідно простіше писати (і розуміти): mpl::if_c<sizeof(int)<=4, foo, bar>::type. Чи не так?
Nawaz

1
@Nawaz: Дійсно, це є кращим у багатьох відношеннях. Я забув про це mpl::if_c. Я оновив приклад, щоб використовувати цей підхід.
Jason R
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.