问题
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?
回答1:
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;
}
回答2:
It seems that the best solution here may be to use a slew of conditionals, 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.
来源:https://stackoverflow.com/questions/28674543/enable-if-in-template-parameters-creates-template-redefinition-error