Лучший способ (или обходной путь) специализировать псевдоним шаблона

В настоящее время я реализую крошечную библиотеку вычислений времени компиляции на основе метапрограммирования.

Если определили базовый класс для операторов, у которого есть результат typedef (я решил использовать встроенные обертки, такие как std::integral_constant в качестве значений вместо необработанных интегральных значений (для обеспечения единого интерфейса вдоль библиотеки) и базового класса n-арных операторов, который проверяет, имеет ли оператор хотя бы один операнд:

template<typename RESULT>
struct operator
{
using result = RESULT;
};

template<typename RESULT , typename... OPERANDS>
struct nary_operator : public operator<RESULT>
{
static_assert( sizeof... OPERANDS > 0 , "An operator must take at least one operand" );
};

Итак, я определил псевдоним для унарных и бинарных операторов:

template<typename OP , typename RESULT>
using unary_operator = nary_operator<RESULT , OP>;

template<typename LHS , typename RHS , typename RESULT>
using binary_operator = nary_operator<RESULT , LHS , RHS>;

Интерфейсы этого оператора используются для определения пользовательских операторов в качестве псевдонима, как операторы сравнения ниже:

template<typename LHS , typename RHS>
using equal = binary_operator<LHS,RHS,bool_wrapper<LHS::value == RHS::value>>;

template<typename LHS , typename RHS>
using not_equal = logical_not<equal<LHS,RHS>>;

template<typename LHS , typename RHS>
using less_than = binary_operator<LHS,RHS,bool_wrapper<LHS::value < RHS::value>>;

template<typename LHS , typename RHS>
using bigger_than = less_than<RHS,LHS>;

template<typename LHS , typename RHS>
using less_or_equal = logical_not<bigger_than<LHS,RHS>>;

template<typename LHS , typename RHS>
using bigger_or_equal = logical_not<less_than<LHS,RHS>>;

Теперь предположим, что мы хотим реализовать наш собственный оператор равенства для нашего собственного класса. Например:

template<typename X , typename Y , typename Z>
struct vec3
{
using x = X;
using y = Y;
using z = Z;
};

Если оператор равенства был сделан наследованием uppon, вместо псевдонима, это может быть легко сделано через специализацию шаблона:

//Equaity comparator implemented through inheritance:
template<typename LHS , typename RHS>
struct equal : public binary_operator<LHS,RHS,bool_wrapper<LHS::value == RHS::value>> {};

//Specialitation of the operator for vec3:

template<typename X1 , typename Y1 , typename Z1 , typename X2 , typename Y2 , typename Z2>
struct equal<vec3<X1,Y1,Z1>,vec3<X2,Y2,Z2>> : public binary_operator<vec3<X1,Y1,Z1>,vec3<X2,Y2,Z2> , bool_wrapper<X1 == X2 && Y1 == Y2 && Z1 == Z2>> {};

я знаю это Псевдоним шаблона не может быть специализированным.
Мой вопрос: Есть ли способ, который не использует dessign наследования вместо псевдонимов шаблона, чтобы специализировать этот тип псевдонимов шаблона?

3

Решение

Шаблон, который я использую, чтобы специализировать псевдонимы шаблонов (или предоставлять рекурсивные псевдонимы), должен иметь соответствующую структуру _impl. Например:

template <typename T>
struct my_alias_impl { /* def'n */ };

template <>
struct my_alias_impl<int> { /* alternate def'n */ };

template <typename T>
using my_alias = my_alias_impl<T>;

Вместо этого пользователям придется специализироваться на my_alias_impl, но остальная часть открытого интерфейса остается чистой.

5

Другие решения

Я знаю, что это старый вопрос, но это общая проблема, и до сих пор в этой теме нет удовлетворительного ответа … поэтому я постараюсь дать один — который касается общей проблемы, а не конкретного вопроса (обратите внимание, что C ++ 14 требуется).

namespace impl
{
template<typename ... Args> struct A;
template<typename T> struct A<T>
{
A() {std::cout<<"I'm an A"<<std::endl;}
};

template<typename ... Args> struct B;
template<typename T, typename V> struct B<T, V>
{
B() {std::cout<<"I'm a B"<<std::endl;}
};
}

template<typename ... Args>
using C = std::conditional_t<sizeof...(Args)==1
, typename impl::A<Args ...>
, typename impl::B<Args ...> >;

int main()
{
C<double> a;            //prints "I'm an A"C<double, int> b;       //prints "I'm a B"}

DEMO

Код должен быть понятен самому себе: основная идея состоит в том, чтобы статически выбирать тип в зависимости от количества аргументов. Вариационные декларации A а также B необходимы, иначе компилятор жалуется, что не может создать экземпляр B с одним параметром или A с двумя параметрами, соответственно

Этот подход, конечно, не совсем общий — просто подумайте об аргументах по умолчанию или специализациях B — но, возможно, его можно расширить и на такие ситуации. Тем не менее я нашел это иногда полезным в моем коде.

2

Псевдоним типа не может быть специализированным, но если ваша цель состоит в упрощении синтаксических признаков, то вы можете сделать это, как я. Я объединяю связанные черты в большой двоичный объект, который является anit-pattern, но затем я использую наследование для самого большого двоичного объекта вместо специализации, чтобы переопределить черту

#define DEFINE_TRAIT(TraitClass,TraitName)                             \
template<typename... T> using TraitName = typename TraitClass<T...>::TraitName

DEFINE_TRAIT(BinaryOpTrait, equal);
DEFINE_TRAIT(BinaryOpTrait, not_equal);
...
...

template<typename LHS , typename RHS>
struct BinaryOpTraitBase
{
using equal = binary_operator<LHS,RHS,bool_wrapper<LHS::value == RHS::value>>;
using not_equal = logical_not<::equal<LHS,RHS>>;
...
...
};
template<typename LHS , typename RHS>
struct BinaryOpTrait : BinaryOpTraitBase <LHS, RHS> {};

typename<>
struct BinaryOpTrait<vec3, vec3> : BinaryOpTraitBase<vec3, vec3>
{
using equal = /* custom op */
};//Usage:
if(equal<int,int>(1,1))
...
if(equal<vec3,vec3>(v1,v1)  //uses specialized
...
0
По вопросам рекламы [email protected]