2

Im в настоящее время реализует крошечную библиотеку вычислений времени компиляции с метапрограммированием.Лучший способ (или обходной путь) для специализации псевдонима шаблона

Если вы определили базовый класс для операторов, у которых есть результат 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>; 

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

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 наследования, вместо наложения спектров, это можно легко сделать с помощью шаблона specialitation:

//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>> {}; 

Я знаю, что template alias cannot be specialized.
Мой вопрос: Есть ли способ, который не должен использовать наследование dessign вместо шаблонных псевдонимов, чтобы специализировать этот тип псевдонимов шаблонов?

+0

У вас уже есть решение. Я не понимаю, что вы ищете (а именно, этот вопрос не объясняет, каковы недостатки существующего решения, которые нам нужно избегать, и каковы цели, которые не удается выполнить существующему решению, мы должны стремиться к). –

+0

@ R.MartinhoFernandes Как я указал в вопросе, я знаю, что это можно сделать через наследование dessign вместо шаблонных псевдонимов. Вопрос только для любопытства. На самом деле путь наследования и способ псевдонимов похожи, но тот факт, что он не может быть сделан напрямую с помощью псевдонимов шаблонов, поражает меня. – Manu343726

+0

@ Manu343726 вы ищете статическое наследование, и это, вероятно, можно сделать с помощью Curiously Recursive Template Pattern. http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern. – IdeaHat

ответ

4

Шаблон, который я использую для специализации псевдонимов шаблонов (или предоставления рекурсивных псевдонимов), должен иметь соответствующую структуру _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 вместо этого, но остальная часть общего интерфейса остается чистой.

+0

В чем преимущество этого подхода? Если вы разделите реализацию с интерфейсом, псевдоним - это просто простой typedef, то есть другое имя для специализации. Почему бы просто не использовать реализацию без псевдонима? – Manu343726

+1

Мой вопрос касается специализации шаблона шаблона. В вашем «решении» использование псевдонима шаблона абсурдно. просто другое имя для специализации, а не псевдоним шаблона (псевдоним с параметром шаблона). – Manu343726

+0

Вы попросили лучший способ (или обходной путь). Это обходной путь. Я не знаю, что вы планируете внедрять внутри своей специализации , поэтому я не удосужился добавить :: type stuff, но я уверен, что вы можете экстраполировать отсюда. – bstamour

0

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

#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 
     ... 
2

Я знаю, что это старый вопрос, но это общая проблема и до сих пор нет удовлетворительного ответа в этой теме ... поэтому я постараюсь дать один - который касается общей проблемы, а не конкретного вопроса (обратите внимание, что требуется 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 - но, возможно, он может быть расширен и для обработки таких ситуаций.Тем не менее, я нашел, что это иногда полезно в моем кодировании.