Шаблон класса, который не зависит от перестановок его аргументов

Рассмотрим шаблон класса и вспомогательные классы перечисления, определенные следующим образом:

enum class Color {Red, Green, Blue}
enum class ShowAxes {False, True}
enum class ShowLabels {False, True}

template< Color, ShowAxes, ShowLabels >
class A
{......};

Вопрос в том, как переопределить класс A, который бы не зависел от перестановок его аргументов. Я использую Dev C++, который поддерживает C++11.

[ИЗМЕНИТЬ]

Например, новая версия A должна поддерживать

A< Color::Red, ShowAxes::True, ShowLabels::True >
A< Color::Red, ShowLabels::True, ShowAxes::True >
A< ShowAxes::True, Color::Red, ShowLabels::True >
A< ShowLabels::True, Color::Red, ShowAxes::True >
A< ShowLabels::True, Color::Red, ShowAxes::True >
A< ShowAxes::True, Color::Red, ShowLabels::True >

версий, и все они идентичны, т.е. генерируют один и тот же класс.


person Vahagn Poghosyan    schedule 02.02.2015    source источник
comment
Специализация шаблона класса С++?   -  person Bryan Chen    schedule 02.02.2015
comment
@BryanChen, вы не можете изменить типы параметров со специализацией   -  person Piotr Skotnicki    schedule 02.02.2015
comment
Я не хочу менять типы параметров. Специализация хорошая идея, но у меня 3! = 6 перестановок. Кроме того, я собираюсь добавить больше параметров.   -  person Vahagn Poghosyan    schedule 02.02.2015
comment
Невозможно с нетиповыми параметрами разных типов. Вы можете взять параметры типа и обернуть значение в такой тип, как std::integral_constant, но он довольно быстро становится многословным.   -  person T.C.    schedule 02.02.2015


Ответы (2)


Это невозможно с вашим текущим интерфейсом, использующим нетиповые параметры.

Вместо этого вы можете взять параметры типа и обернуть значения в std::integral_constant:

template<class X, class Y, class Z>
class A { /* stuff */ };

// use as:
A<std::integral_constant<Color, Color::Red>,
  std::integral_constant<ShowAxes, ShowAxes::True>,
  std::integral_constant<ShowLabels, ShowLabels::True>> a;

Это довольно многословно, поэтому вы можете написать макрос:

#define AS_IC(Value) std::integral_constant<decltype(Value), Value>

и переписать как

A<AS_IC(Color::Red), AS_IC(ShowAxes::True), AS_IC(ShowLabels::True)> a;

Извлечь значение нужного типа из списка integral_constants очень просто:

template<class Result, class...>
struct extract;

template<class Result, Result Value, class... Tail>
struct extract<Result, std::integral_constant<Result, Value>, Tail...> : std::integral_constant<Result, Value> {};

template<class Result, class Head, class... Tail>
struct extract<Result, Head, Tail...> : extract<Result, Tail...> {};

Тогда вы можете сделать

// inside the definition of A
static constexpr Color col = extract<Color, X, Y, Z>::value;

Демо.

Это, однако, не создает тот же класс, но вы можете создать шаблон класса A_impl, который ведет себя как ваш A с нетиповыми параметрами и содержит реальную реализацию, а затем сделать A шаблоном псевдонима:

template< Color, ShowAxes, ShowLabels >
class A_impl
{/* stuff */};

template<class X, class Y, class Z>
using A = A_impl<extract<Color, X, Y, Z>::value,
                 extract<ShowAxes, X, Y, Z>::value,
                 extract<ShowLabels, X, Y, Z>::value>;

Теперь дано

A<AS_IC(Color::Red), AS_IC(ShowAxes::True), AS_IC(ShowLabels::True)> a;
A<AS_IC(Color::Red), AS_IC(ShowLabels::True), AS_IC(ShowAxes::True)> b;

a и b имеют один и тот же тип. Демо.

В качестве альтернативы вы также можете использовать decltype и шаблоны функций перегрузки, но это требует добавления объявления шаблона функции для каждого возможного порядка типов:

template< Color c, ShowAxes a, ShowLabels l>
A<c,a,l> A_of();

template< ShowAxes a, ShowLabels l, Color c>
A<c,a,l> A_of();

// etc.

decltype(A_of<Color::Red, ShowAxes::True, ShowLabels::True>()) a1;
decltype(A_of<ShowAxes::True, ShowLabels::True, Color::Red>()) a2;
person T.C.    schedule 02.02.2015
comment
Идея std::integral_constant и extract выглядит неплохо. Подумаю об оптимизации кода. - person Vahagn Poghosyan; 02.02.2015
comment
У меня небольшой (может быть тривиальный) вопрос. Можно ли заменить макрос AS_IC недирективным персоналом, скажем, шаблоном класса или функцией шаблона? - person Vahagn Poghosyan; 02.02.2015
comment
@VaagnPoghosyan Нет известного мне способа без макросов без добавления большого количества шаблонов при объявлении A. - person T.C.; 03.02.2015

Возможно, используя std::is_same. Затем вы можете упростить свой код следующим образом:

template <typename A, typename B, typename C>
class X
{
public:
    X() {
        static_assert(
            std::is_same<A, Color>::value ||
            std::is_same<B, Color>::value ||
            std::is_same<C, Color>::value,
            "nope");
        // other assertions here!
        // also, make sure your types are different ;)
    }
    X(A a, B b, C c) : X() {
        // your code here
    }
};

template <typename A, typename B, typename C>
X<A, B, C> make(A a, B b, C c) {
    // possible verifications here
    return X<A, B, C>(a, b, c);
}

int main() {
    auto a = make(Color::Red, ShowAxes::true, ShowLabels::True);
    return 0;
}

Вы можете проверить все свои типы A, B и C.

Извините, но другого решения я не вижу. :/

person vincentp    schedule 02.02.2015
comment
Я добавил некоторый текст в вопрос для подробного объяснения моей цели. - person Vahagn Poghosyan; 02.02.2015
comment
Спасибо, но например make(Color::Red, ShowAxes::True, ShowLabels::True) и make(ShowAxes::True, Color::Red, ShowLabels::True) дают разные результаты. Они должны генерировать один и тот же класс. - person Vahagn Poghosyan; 02.02.2015
comment
Может ли наследование решить проблему? Если класс X наследуется от базового класса, они могут быть одного типа благодаря полиморфизму. - person vincentp; 02.02.2015