The idea is simple and straight-forward:
Keep breaking the n
dimensional vector into n-1
dimensional constituent vectors, until you have access to the primitive-datatype objects. Then add them all.
The problem is, how to infer the return type?
It can be done like this, but it already assumes the datatype of summing variable (return-type):
typedef int SumType;
template <class T>
T Sum (const T x)
{
return x;
}
template <class T>
SumType Sum (const std::vector<T>& v)
{
SumType sum = 0;
for (const auto& x: v)
sum += Sum(x);
return sum;
}
But I don't want to do it like above. I feel like its against the spirit of meta-programming.
We must infer the return-type by keeping dissecting the vector into its constituent vectors until we reach the primitive-datatype objects, and then choose the return-type as the primitive-datatype.
Is it possible in C++ ? (I'm noob in meta-programming)
PS
std::accumulate()
from <numeric>
could have been helpful, but it by-passes the problem by inferring the return-type from its third argument __init
.
What we can do is use TC 's data_type
class to get the the underlying type. That is defined as
template<class T> struct voider { typedef void type; }; template<class T, class = void> struct data_type { typedef T type; }; template<class T> struct data_type<T, typename voider<typename T::value_type>::type> : data_type<typename T::value_type> {};
Using that we can modify the primary Sum
to
template <class T, class Ret = typename data_type<std::vector<T>>::type>
Ret Sum (const std::vector<T>& v)
{
Ret sum = 0;
for (const auto& x: v)
sum += Sum(x);
return sum;
}
So then you can use something like
int main()
{
std::cout << Sum(std::vector<std::vector<std::vector<int>>>{{{1},{2},{3}},{{4},{5},{6}}});
}
which outputs
21
This can be done without any template meta programming. You can let the compiler infer the type using auto
and decltype
:
template <class T>
T Sum(const T x) {
return x;
}
template <class T>
auto Sum(const std::vector<T> &v) {
decltype(Sum(v[0])) sum = 0;
for (const auto &x : v)
sum += Sum(x);
return sum;
}
The return type of Sum
is automatically deduced from sum
and the type of sum
is whatever Sum(v[0])
returns. Eventually you will end up with the first version of Sum
which returns T
and the compiler knows that type.
You've almost figured out the answer for yourself. Pay attention to this line:
sum += Sum(x);
The type of sum
, which is what we're after, must be something compatible for assignment with the result of our recursive call to Sum
. One such type, given your requirements, is certainly the result type of the call.
We don't have to rely on just a fuzzy feeling though. Meta-programming is, after all, programming. You may not have realised it, but your problem is one of well-founded recursion which means that the principle of induction can guide us towards an answer.
in the base case, we have a numerical, non-vector element_type element;
, meaning our result type is… element_type
. you've in fact already managed this step, it's the first overload:
template<typename T> T Sum(T element);
in the recursive case we have:
std::vector<element_type> vec;
the induction hypothesis, ie:
// given element_type element; // we know the following is well-formed and a numerical type using recursive_result_type = decltype( Sum(element) );
Since the vector elements have type element_type
, the induction hypothesis gives us that the result of calling Sum
on them has all the properties we want. (The justification for our +=
intuition is rooted here.) We have our anser: we use recursive_result_type
as-is.
Now as it turns out that second overload cannot just be written eg like so:
// doesn't behave as expected
template<typename Element>
auto Sum(std::vector<Element> const& vec) -> decltype( Sum(vec.front()) );
The reason being that the current Sum
overload being declared is not in scope in the return type (even though it is in the definition body). One way to work around that is to rely on class scope, which is more accommodating:
// defining a functor type with operator() overloads
// would work just as well
struct SumImpl {
template<typename Element>
static T apply(Element element)
{ return element; }
template<typename Element>
static auto apply(std::vector<Element> const& vec)
-> decltype( apply(vec.front()) )
{
using result_type = decltype( apply(vec.front()) );
result_type sum = 0;
for(auto const& element: vec) {
sum += apply(element);
}
return sum;
}
};
template<typename Arg>
using sum_result_t = decltype( SumImpl::apply(std::declval<Arg const&>()) );
template<typename Arg>
sum_result_t<Arg> Sum(Arg const& arg)
{ return SumImpl::apply(arg); }
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.