Another problem with decltype
Вопрос
//THIS IS JUST A FRAGMENT OF A static_numeric_limits.h for the purpose of this example
#include <limits.h>
template<class T>
struct static_numeric_limits;
template<>
struct static_numeric_limits<signed char>
{/*min was outside of range for enum*/
static const signed char min = SCHAR_MIN,
max = SCHAR_MAX;
};
/*This "surplus" template is here for the reason that char is threated differently from signed char */
template<>
struct static_numeric_limits<char>
{/*min was outside of range for enum*/
static const char min = SCHAR_MIN,
max = SCHAR_MAX;
};
template<>
struct static_numeric_limits<unsigned char>
{
static const unsigned char min = 0x0,
max = UCHAR_MAX;
};
///REAL PROBLEM STARTS FROM HERE
template<class IntType,IntType low_range = static_numeric_limits<IntType>::min>
struct Int
{
Int():value_(IntType())
{}
Int(const IntType& pattern)
{
value_ = (pattern);
}
constexpr inline IntType getValue()const
{
return value_;
}
private:
IntType value_;
};
template<class IntType,class IntType_1>
auto operator+
(Int<IntType>& lhs, Int<IntType_1>& rhs)
-> Int<decltype(lhs.getValue() + rhs.getValue())>//HERE IS THE PROBLEM
{
return lhs.getValue() + rhs.getValue();
}
Error (from VS2010)
error C2027: use of undefined type 'static_numeric_limits<T>'
Error (from gcc 4.6)
error: 'decltype ((lhs->getValue() + rhs->getValue()))' is not a valid type for a template constant parameter
Why doesn't this work as I thought it would?
Решение
The error here is what type decltype
is deducing from your expression; unfortunately the error messages aren't clear about it, and it's actually a bit of a tricky problem.
Consider the type of the expression 0 + 0
. It's an int
, yes, but more importantly it's an rvalue (informally, it's a temporary). This means that decltype(0 + 0)
is not int
, but int&&
. Now consider that your code isn't any different, in this regard: you still have an rvalue.
The problem is that template non-type parameters cannot be rvalue references, so you cannot have Int<int&&>
, because of the second parameter's type . What you can do, though is this:
#include <type_traits>
// ...
template <class IntType, class IntType_1>
auto operator+(const Int<IntType>& lhs, // be const-correct!
const Int<IntType_1>& rhs)
-> Int<typename std::remove_reference<
decltype(lhs.getValue() + rhs.getValue())>::type>
{
return lhs.getValue() + rhs.getValue();
}
This takes the reference off int&&
, giving you the bare int
type. Hopefully gcc's error message makes a bit more sense: it's trying to tell you that you can't use int&&
for your non-type parameter.
Another problem, though probably a non-issue, is that integer arithmetic undergoes what's called the usual arithmetic conversions. So the result of adding the values of two Int<char>
's is actually going to be an int
, so your return type should be Int<int>
(and is, with the fixed code).
The problem, then, is that you haven't defined static_numeric_limits<int>
. But like I said, I suspect this is a non-issue and you do actually have it defined, just not displayed in your question.