Шаблон класса, который не зависит от перестановок его аргументов
Рассмотрим шаблон класса и вспомогательные перечислимые классы, определенные следующим образом:
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 >
версии, и все они идентичны, т.е. они генерируют один и тот же класс.
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_constant
s прост:
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;
Может быть, используя 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.
Извините, но я не вижу другого решения.:/