Why the code below does not compile?
template
T sum(T t){
return t;
}
template
auto sum(T t, U... u
Here we forward the work to helper types:
namespace details {
template
struct sum_t {};
template
struct sum_t {
T operator()(T t)const{ return std::forward(t); }
};
template
struct sum_t {
auto operator()(T t, Ts...ts)const
-> decltype( std::declval() + sum_t{}(std::declval()...) )
{
return std::forward(t) + sum_t{}(std::forward(ts)...);
}
};
}
template
auto sum(Ts...ts)
-> decltype( details::sum_t{}(std::declval()...) )
// -> std::result_of_t(Ts...)>
// above line is C++14 and cleaner version of previous line
{
return details::sum_t{}(std::forward(ts)...);
}
the basic problem was that a template function cannot see itself when calculating its own return type in a -> decltype
clause.
There are a few work arounds. The above should work, because a template class can see other specializations of its partial specialization in its own body. Another approach would be to use Koenig lookup (ADL) to defer the searching for its recursive call until the point of instantiation, where it can find itself. I find that second approach more confusing.
If I was to write my own sum
for production, I'd have it optionally take the type I expect it to return, and if it did it would accept a zero length sum (creating a default instance), but not requires that the type be default constructable if I pass 1 or more arguments. But I like over-engineered generic code:
template{},
R0,
std::result_of_t(Ts...)>
>>
R sum(Ts...ts)
{
return details::sum_t{}(std::forward(ts)...);
}
where I modify sum_t
to take the return type as the first parameter:
namespace details {
template
struct sum_t {
R operator()()const{ return {}; }
};
template
struct sum_t {
using R0 = std::conditional_t{},R,T>;
R0 operator()(T t)const{ return std::forward(t); }
};
template
struct sum_t {
using R0 = std::conditional_t<
!std::is_same{},
R,
decltype( std::declval() + sum_t{}(std::declval()...) )
>;
R0 operator()(T t, Ts...ts)const
{
return std::forward(t) + sum_t{}(std::forward(ts)...);
}
};
}
which makes me want to be able to write "do this sum, but cast each sub-sum to R
before continuing" or somesuch.
In C++1z, you'll want to use a fold-expression instead. Being able to set R
is still useful, as if you are adding up an expression template, it may only be valid as an expression template until the end of the current scope.
To fix this problem in C++14, you may have to use continuation passing style with the R
return value.
We could then fold return type deduction into the game to allow
Matrix m = sum( many_matrices... );
to work in Eigen (for example).
When you first start to write generic code, you have to ask yourself "how deep down the rabbit hole do we want to go?"