Partial specialization of class template for a type that appears in any
position of a variadic template parameter pack
I have defined a type that acts as an integer. I want to define a
specialization for std::common_type for my type. However, this
specialization should be able to give the common_type of ranged_integer
(my class) in combination with any number of other arguments that are
either other ranged_integer or built-in integer types. I want the
following code to all be valid:
std::common_type<ranged_integer<1, 10>>::type
std::common_type<ranged_integer<1, 10>, int>::type
std::common_type<int, long, ranged_integer<1, 10>>::type
std::common_type<int, int, long, short, long long, short,
ranged_integer<1, 10>, int, short, short, short, ..., short,
ranged_integer<1, 10>>::type
My first attempt at solving this problem was by using enable_if. However,
I realized that this would not allow me to distinguish from the library
definition of common_type, as what I had was essentially
#include <type_traits>
class C {};
template<typename T, typename... Ts>
class contains_c {
public:
static constexpr bool value = contains_c<T>::value or
contains_c<Ts...>::value;
};
template<typename T>
class contains_c<T> {
public:
static constexpr bool value = std::is_same<T, C>::value;
};
namespace std {
template<typename... Args, typename
std::enable_if<contains_c<Args...>::value>::type>
class common_type<Args...> {
public:
using type = C;
};
} // namespace std
int main() {
}
Where the 'partial specialization' is really just "any arguments", which
is no more specialized than what we have.
So it seems like the only solution is to require my users to do one of the
following:
always put the ranged_integer as the first argument to common_type
always use my make_ranged(built-in integer value) function to convert
their integers to ranged_integer (so don't have a specialization of
common_type for built-in types in combination with ranged_integer)
never put ranged_integer in a position greater than N, where N is some
number I determine, similar to Visual Studio's old variadic template
work-around
3 would look something like this:
// all_ranged_integer_or_integral and all_are_integral defined elsewhere
with obvious definitions
template<intmax_t minimum, intmax_t maximum, typename... Ts, typename =
type std::enable_if<all_ranged_integer_or_integral<Ts...>::value>::type>
class common_type<ranged_integer<minimum, maximum>, Ts...> {
};
template<typename T1, intmax_t minimum, intmax_t maximum, typename... Ts,
typename = typename std::enable_if<all_are_integral<T1>::value>::type,
typename = typename
std::enable_if<all_ranged_integer_or_builtin<Ts...>::value>::type>
class common_type<T1, ranged_integer<minimum, maximum>, Ts...> {
};
template<typename T1, typename T2, intmax_t minimum, intmax_t maximum,
typename... Ts, typename = typename std::enable_if<all_are_integral<T1,
T2>::value>::type, typename = typename
std::enable_if<all_ranged_integer_or_builtin<Ts...>::value>::type>
class common_type<T1, T2, ranged_integer<minimum, maximum>, Ts...> {
};
// etc.
Is there a better way to accomplish this (template specialization when all
the types meet one condition and any of the types meet another condition)
for a class that I cannot change the original definition for?
No comments:
Post a Comment