In this answer what I really wanted to do is define a typename
in my template parameters which could be used in the cast and return.
So this:
template <typename T>
typename std::enable_if<sizeof(unsigned char) == sizeof(T), unsigned char>::type caster(T value){ return reinterpret_cast<unsigned char&>(value); }
Would become this:
template <typename T, typename R = std::enable_if<sizeof(unsigned char) == sizeof(T), unsigned char>::type >
R caster(T value){ return reinterpret_cast<R&>(value); }
This works and behaves as desired for a single template specialization, but say that I add another specialization:
template <typename T, typename R = std::enable_if<sizeof(short) == sizeof(T), short>::type>
R caster(T value){ return reinterpret_cast<R&>(value); }
Now I get an error:
error C2995: 'R caster(T)' : function template has already been defined
Is there a way to convince the compiler that only one of these specializations will actually build for any given call?
No, there isn't. Template default arguments are just that, defaults. Any user could call caster<short, short>
, which would match both overloads.
However, it would be possible to add more dummy arguments.
template <typename T,
typename R = typename std::enable_if<sizeof(unsigned char) == sizeof(T), unsigned char>::type >
R caster(T value) { return reinterpret_cast<R&>(value); }
template <typename T,
typename R = typename std::enable_if<sizeof(short) == sizeof(T), short>::type,
typename = void>
R caster(T value) { return reinterpret_cast<R&>(value); }
(Also note the added typename
.)
However, since all bodies are identical, I probably wouldn't go with overloads.
template <std::size_t N>
struct cast_result;
template <>
struct cast_result<sizeof(std::uint8_t)> {
typedef std::uint8_t type;
};
template <>
struct cast_result<sizeof(std::uint16_t)> {
typedef std::uint16_t type;
};
...
template <typename T, typename R = typename cast_result<sizeof(T)>::type>
R caster(T value) {
return reinterpret_cast<R&>(value);
}
A final note: this use of reinterpret_cast
is a violation of the aliasing rules. However, that's easily fixed:
template <typename T, typename R = typename cast_result<sizeof(T)>::type>
R caster(T value) {
R result;
std::memcpy(&result, &value, sizeof result);
return result;
}
It seems that the best solution here may be to use a slew of conditional
s, which would prevent me from having to fool with template specializations:
template <typename T, typename R = std::conditional<sizeof(T) == sizeof(unsigned char),
unsigned char,
conditional<sizeof(T) == sizeof(unsigned short),
unsigned short,
conditional<sizeof(T) == sizeof(unsigned long),
unsigned long,
enable_if<sizeof(T) == sizeof(unsigned long long), unsigned long long>::type>::type>::type>::type>
R caster(T value){ return reinterpret_cast<R&>(value); }
My apologies to the reader cause it's like reading nested ternaries. However I'm currently unaware of a cleaner way to handle this.
This sadly still doesn't prevent the user from stomping on all my defaulting by providing his own second template parameter as mentioned by hvd.
EDIT:
I've asked another question here which has a solution that doesn't require placing the typename
in the template definition and doesn't require declaring the type twice.