I 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 provide the common_type bounded_integer (my class) in combination with any number of other arguments, which are either other bounded integer or inline integer types. I want the following code to be valid:
std::common_type<bounded_integer<1, 10>>::type std::common_type<bounded_integer<1, 10>, int>::type std::common_type<int, long, bounded_integer<1, 10>>::type std::common_type<int, int, long, short, long long, short, bounded_integer<1, 10>, int, short, short, short, ..., short, bounded_integer<1, 10>>::type
My first attempt to solve this problem was with enable_if. However, I realized that this would not allow me to distinguish from the definition of the common_type library, since 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() { }
If “partial specialization” is simply “any arguments” that are not 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 puts bounded_integer as the first argument to common_type
- always use the make_bounded function (built-in integer value) to convert your integers to bounded_integer (so you don't have the common_type specialization for built-in types combined with bounded_integer)
- never put bounded_integer in a position greater than N, where N is some number that I define, similar to the old work with Visual Studio template templates
3 will look something like this:
// all_bounded_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_bounded_integer_or_integral<Ts...>::value>::type> class common_type<bounded_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_bounded_integer_or_builtin<Ts...>::value>::type> class common_type<T1, bounded_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_bounded_integer_or_builtin<Ts...>::value>::type> class common_type<T1, T2, bounded_integer<minimum, maximum>, Ts...> { }; // etc.
Is there a better way to achieve this (template specialization, when all types satisfy one condition, and any of the types meets another condition) for a class that I cannot change the original definition for?
EDIT:
Based on the answers, I was not clear enough about my problem.
First, the expected behavior:
If someone calls std :: common_type with all types being an instance of bounded_integer or a built-in numeric type, I want the result to be a bounded integrator that has a minimum of all possible minima and a maximum of all possible maxima.
Problem:
I have a working solution when someone calls std :: common_type on any number of bounded_integer. However, if I am only specializing in a version with two arguments, then I am faced with the following problem:
std::common_type<int, unsigned, bounded_integer<0, std::numeric_limits<unsigned>::max() + 1>
should give me
bounded_integer<std::numeric_limits<int>::min(), std::numeric_limits<unsigned>::max() + 1>
However, it is not. First, he applies common_type to int and unsigned , which follows the standard integral promotion rules, providing unsigned . It then returns the result of common_type with unsigned and my bounded_integer , giving
bounded_integer<0, std::numeric_limits<unsigned>::max() + 1>
So, adding unsigned to the middle of the package of parameters, although it should have absolutely no effect on the type of result (its ranges are completely contained in the ranges of all other types), it still affects the result. The only way to prevent this is to specialize std::common_type for any number of built-in integers, followed by bounded_integer , followed by any number of built-in integers or bounded_integer .
My question is: how can I do this without the need for approximation, manually writing out an arbitrary number of parameters, followed by a bounded_integer , followed by a package of parameters, or is this impossible?
EDIT 2:
The reason common_type will give incorrect values can be explained by these standard considerations (quoted from N3337)
common_type of int and unsigned is unsigned . For example: http://ideone.com/9IxKIW . The standard ones can be found in § 20.9.7.6/3, where the common_type two values
typedef decltype(true ? declval<T>() : declval<U>()) type;
§ 5.16 / 6 states that
The second and third operands are of arithmetic or enumeration type; ordinary arithmetic conversions are performed to bring them to a common type, and the result of that type.
Ordinary arithmetic conversions are defined in § 5/9 as
Otherwise, if the operand having an unsigned integer type has a rank greater than or equal to the ranks of the type of the other operand, the operand with a signed integer type must be converted to the operand type with an unsigned integer.