Best way (Or workaround) to specialize a template alias

爷,独闯天下 提交于 2020-01-14 13:32:12

问题


Im currently implementing a tiny metaprogramming-based compile-time computations library.

If have defined a base class for operators, wich has a result typedef (I have decided to use integral wrappers like std::integral_constant as values instead of raw integral values, to provide an uniform interface along the library), and a n-ary operator base class, that checks if the operators has at least one operand:

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" );
};

So I defined alias for unary and binary operators:

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

That operator interfaces are used to define custom operators as alias, like comparison operators bellow:

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

Now suppose we want to implement our custom equality operator for our own class. For example:

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

If the equality operator was made uppon inheritance, instead of aliasing, this could be easily done through template 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>> {}; 

I know that template alias cannot be specialized.
My quesion is: Is there a way, that is not to use inheritance dessign instead of template aliases, to specialize this kind of template aliases?


回答1:


The pattern I use to specialize template aliases (or provide recursive aliases) is to have a corresponding _impl struct. For example:

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

Users would have to specialize on my_alias_impl instead, but the rest of the public interface remains clean.




回答2:


I know this is an old question, but it's a common problem and there's no satisfying answer in this thread up to now ... so I'll try to give one -- which addresses the general problem rather than the specific question (note that C++14 is required).

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

The code should be self-explaining: the main idea is to statically choose the type based on the number of arguments. The variadic declarations of A and B are necessary, otherwise the compiler complains that it can't instantiate a B with a single parameter or an A with two parameters, resp.

This approach is surely not completely general -- just think of default arguments or specializations of B -- but maybe it can be extended also to handle such situations. Nevertheless I found it sometimes useful in my coding.




回答3:


Type-alias cant be specialized but if your aim is to simplifiy traits syntactically then you can do it as I do. I combine related traits together in a blob which is anit-pattern but then I use inheritence on the blob itself instead of specialization to override a trait

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


来源:https://stackoverflow.com/questions/17815276/best-way-or-workaround-to-specialize-a-template-alias

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!