Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

class template specialization that accepts all versions of const / volatile qualifications and & vs &&

I am specializing std::common_type for my type. I defined the following specialization:

common_type<my_type, my_type>

And all is well. Then someone comes along and calls std::common_type<my_type, my_type &>. The default version acts the same if you pass a reference vs. not a reference (as it calls std::decay on the types). However, it doesn't defer to the non-reference version of std::common_type, which I would need to work correctly. Is there a better way than having to do something like this (leaving out rvalue-reference to const for simplicity):

common_type<my_type, my_type>
common_type<my_type, my_type &>
common_type<my_type, my_type const &>
common_type<my_type, my_type volatile &>
common_type<my_type, my_type const volatile &>
common_type<my_type, my_type &&>
common_type<my_type, my_type volatile &&>
common_type<my_type &, my_type>
common_type<my_type const &, my_type>
common_type<my_type volatile &, my_type>
common_type<my_type const volatile &, my_type>
common_type<my_type &&, my_type>
common_type<my_type volatile &&, my_type>
common_type<my_type &, my_type &>
common_type<my_type &, my_type const &>
common_type<my_type &, my_type volatile &>
...

Surely there is a better way? By my count, that is 49 possible versions if we ignore const && and const volatile &&

Note: my_type is actually a class template itself, so the specialize actually looks more like

template<intmax_t lhs_min, intmax_t lhs_max, intmax_t rhs_min, intmax_t rhs_max>
class common_type<my_type<lhs_min, lhs_max>, my_type<rhs_min, rhs_max>>

Where the result is my_type<min(lhs_min, rhs_min), max(lhs_max, rhs_max)>

The solution would be fairly straightforward if I had full control over the primary template definitions, but I obviously cannot change std::common_type.

like image 728
David Stone Avatar asked Nov 23 '13 19:11

David Stone


People also ask

What is template specialization used for?

It is possible in C++ to get a special behavior for a particular data type. This is called template specialization. Template allows us to define generic classes and generic functions and thus provide support for generic programming.

What is called specialization in template?

The act of creating a new definition of a function, class, or member of a class from a template declaration and one or more template arguments is called template instantiation. The definition created from a template instantiation is called a specialization.

How many types of specialization are there in C++?

How many types of specialization are there in c++? Explanation: There are two types of specialization. They are full specialization and partial specialization.

What is the syntax for explicit class specialization?

What is the syntax to use explicit class specialization? Explanation: The class specialization is creation of explicit specialization of a generic class. We have to use template<> constructor for this to work. It works in the same way as with explicit function specialization.


1 Answers

As far as I know, you don't need to completely specialize both sides of the binary common_type. This allows reducing the amount of specializations to 12 for one side. If you only need a common type between specializations of my_type and my_type, than it's sufficient to specialize on one side. Otherwise, you'd had to clone them on the right side, yielding 24 specializations.

struct my_type;
struct unique_t;

#include <type_traits>

template<class L, class R, class = void>
struct mytype_common_type
{
    // not many specializations are required here,
    // as you can use std::decay and don't have to use "Exact Matches"
    using type = unique_t;
};

namespace std
{
    template<class T> struct common_type<my_type, T>
    : mytype_common_type<my_type, T> {};
    template<class T> struct common_type<my_type const, T>
    : mytype_common_type<my_type, T> {};
    template<class T> struct common_type<my_type volatile, T>
    : mytype_common_type<my_type, T> {};
    template<class T> struct common_type<my_type const volatile, T>
    : mytype_common_type<my_type, T> {};

    template<class T> struct common_type<my_type&, T>
    : mytype_common_type<my_type, T> {};
    template<class T> struct common_type<my_type const&, T>
    : mytype_common_type<my_type, T> {};
    template<class T> struct common_type<my_type volatile&, T>
    : mytype_common_type<my_type, T> {};
    template<class T> struct common_type<my_type const volatile&, T>
    : mytype_common_type<my_type, T> {};

    template<class T> struct common_type<my_type&&, T>
    : mytype_common_type<my_type, T> {};
    template<class T> struct common_type<my_type const&&, T>
    : mytype_common_type<my_type, T> {};
    template<class T> struct common_type<my_type volatile&&, T>
    : mytype_common_type<my_type, T> {};
    template<class T> struct common_type<my_type const volatile&&, T>
    : mytype_common_type<my_type, T> {};
}

template<class T>
using Decay = typename std::decay<T>::type;

int main()
{
    static_assert(std::is_same<unique_t,
                    std::common_type<my_type const volatile&&, int>::type
                  >{}, "!");
}
like image 108
dyp Avatar answered Oct 04 '22 01:10

dyp