arithmetic sum of function parameters [duplicate] - c++

I want to write a simple adder (for giggles) that adds up every argument and returns a sum with appropriate type.
Currently, I've got this:
#include <iostream>
using namespace std;
template <class T>
T sum(const T& in)
{
return in;
}
template <class T, class... P>
auto sum(const T& t, const P&... p) -> decltype(t + sum(p...))
{
return t + sum(p...);
}
int main()
{
cout << sum(5, 10.0, 22.2) << endl;
}
On GCC 4.5.1 this seems to work just fine for 2 arguments e.g. sum(2, 5.5) returns with 7.5. However, with more arguments than this, I get errors that sum() is simply not defined yet. If I declare sum() like this however:
template <class T, class P...>
T sum(const T& t, const P&... p);
Then it works for any number of arguments, but sum(2, 5.5) would return integer 7, which is not what I would expect.
With more than two arguments I assume that decltype() would have to do some sort of recursion to be able to deduce the type of t + sum(p...). Is this legal C++0x? or does decltype() only work with non-variadic declarations? If that is the case, how would you write such a function?

I think the problem is that the variadic function template is only considered declared after you specified its return type so that sum in decltype can never refer to the variadic function template itself. But I'm not sure whether this is a GCC bug or C++0x simply doesn't allow this. My guess is that C++0x doesn't allow a "recursive" call in the ->decltype(expr) part.
As a workaround we can avoid this "recursive" call in ->decltype(expr) with a custom traits class:
#include <iostream>
#include <type_traits>
using namespace std;
template<class T> typename std::add_rvalue_reference<T>::type val();
template<class T> struct id{typedef T type;};
template<class T, class... P> struct sum_type;
template<class T> struct sum_type<T> : id<T> {};
template<class T, class U, class... P> struct sum_type<T,U,P...>
: sum_type< decltype( val<const T&>() + val<const U&>() ), P... > {};
This way, we can replace decltype in your program with typename sum_type<T,P...>::type and it will compile.
Edit: Since this actually returns decltype((a+b)+c) instead of decltype(a+(b+c)) which would be closer to how you use addition, you could replace the last specialization with this:
template<class T, class U, class... P> struct sum_type<T,U,P...>
: id<decltype(
val<T>()
+ val<typename sum_type<U,P...>::type>()
)>{};

Apparently you can't use decltype in a recursive manner (at least for the moment, maybe they'll fix it)
You can use a template structure to determine the type of the sum
It looks ugly but it works
#include <iostream>
using namespace std;
template<typename... T>
struct TypeOfSum;
template<typename T>
struct TypeOfSum<T> {
typedef T type;
};
template<typename T, typename... P>
struct TypeOfSum<T,P...> {
typedef decltype(T() + typename TypeOfSum<P...>::type()) type;
};
template <class T>
T sum(const T& in)
{
return in;
}
template <class T, class... P>
typename TypeOfSum<T,P...>::type sum(const T& t, const P&... p)
{
return t + sum(p...);
}
int main()
{
cout << sum(5, 10.0, 22.2) << endl;
}

C++14's solution:
template <class T, class... P>
decltype(auto) sum(const T& t, const P&... p){
return t + sum(p...);
}
Return type is deducted automatically.
See it in online compiler
Or even better if you want to support different types of references:
template <class T, class... P>
decltype(auto) sum(T &&t, P &&...p)
{
return std::forward<T>(t) + sum(std::forward<P>(p)...);
}
See it in online compiler
If you need a natural order of summation (that is (((a+b)+c)+d) instead of (a+(b+(c+d)))), then the solution is more complex:
template <class A>
decltype(auto) sum(A &&a)
{
return std::forward<A>(a);
}
template <class A, class B>
decltype(auto) sum(A &&a, B &&b)
{
return std::forward<A>(a) + std::forward<B>(b);
}
template <class A, class B, class... C>
decltype(auto) sum(A &&a, B &&b, C &&...c)
{
return sum( sum(std::forward<A>(a), std::forward<B>(b)), std::forward<C>(c)... );
}
See it in online compiler

Another answer to the last question with less typing by using C++11's std::common_type: Simply use
std::common_type<T, P ...>::type
as return type of your variadic sum.
Regarding std::common_type, here is an excerpt from http://en.cppreference.com/w/cpp/types/common_type:
For arithmetic types, the common type may also be viewed as the type
of the (possibly mixed-mode) arithmetic expression such as T0() + T1()
+ ... + Tn().
But obviously this works only for arithmetic expressions and doesn't cure the general problem.

I provide this improvement to the accepted answer. Just two structs
#include <utility>
template <typename P, typename... Ps>
struct sum_type {
using type = decltype(std::declval<P>() + std::declval<typename sum_type<Ps...>::type>());
};
template <typename P>
struct sum_type<P> {
using type = P;
};
Now just declare your functions as
template <class T>
auto sum(const T& in) -> T
{
return in;
}
template <class P, class ...Ps>
auto sum(const P& t, const Ps&... ps) -> typename sum_type<P, Ps...>::type
{
return t + sum(ps...);
}
With this, your test code now works
std::cout << sum(5, 10.0, 22.2, 33, 21.3, 55) << std::endl;
146.5

Right way to do:
#include <utility>
template <typename... Args>
struct sum_type;
template <typename... Args>
using sum_type_t = typename sum_type<Args...>::type;
template <typename A>
struct sum_type<A> {
using type = decltype( std::declval<A>() );
};
template <typename A, typename B>
struct sum_type<A, B> {
using type = decltype( std::declval<A>() + std::declval<B>() );
};
template <typename A, typename B, typename... Args>
struct sum_type<A, B, Args...> {
using type = sum_type_t< sum_type_t<A, B>, Args... >;
};
template <typename A>
sum_type_t<A> sum(A &&a)
{
return (std::forward<A>(a));
}
template <typename A, typename B>
sum_type_t<A, B> sum(A &&a, B &&b)
{
return (std::forward<A>(a) + std::forward<B>(b));
}
template <typename A, typename B, typename... C>
sum_type_t<A, B, C...> sum(A &&a, B &&b, C &&...args)
{
return sum( sum(std::forward<A>(a), std::forward<B>(b)), std::forward<C>(args)... );
}
https://coliru.stacked-crooked.com/a/a5a0e8019e40b8ba
This completely preserves resulting type of operations (even r-value referenceness). The order of operations is natural: (((a+b)+c)+d).

For C++17:
template <class... P>
auto sum(const P... p){
return (p + ...);
}
int main()
{
std::cout << sum(1, 3.5, 5) << std::endl;
return EXIT_SUCCESS;
}
Read about folding expressions.

Related

Transform typelist with function at runtime

I have a typelist. I would like to create a tuple with the results of calling a function on each type in that list and then use that as arguments to another functor. So something like this:
template<typename F>
struct function_traits;
template<typename T, typename R, typename... Args>
struct function_traits<R(T::*)(Args...) const> {
using return_type = R;
using param_types = std::tuple<Args...>;
};
template<typename T> struct function_traits : public
function_traits<decltype(&T::operator())> {};
template <typename T>
T* get_arg(int id)
{
// Actual implementation omitted. Uses the id parameter to
// do a lookup into a table and return an existing instance
// of type T.
return new T();
}
template <typename Func>
void call_func(Func&& func, int id)
{
using param_types = function_traits<Func>::param_types>;
func(*get_arg<param_types>(id)...); // <--- Problem is this line
}
call_func([](int& a, char& b) { }, 3);
The problem is that func(*get_arg<param_types>(id)...); doesn't actually compile since param_types is a tuple and not a parameter pack. The compiler generates this error: "there are no parameter packs available to expand". What I would liked to have happened is for that line to expand to:
func(*get_arg<int>(id), *get_arg<char>(id));
And to have that work for any number of arguments. Is there any way to get that result?
This question seems similar but does not solve my problem by itself: "unpacking" a tuple to call a matching function pointer. I have a type list and from that I want to generate a list of values to use as function arguments. If I had the list of values I could expand them and call the function as outlined in that question, but I do not.
Not sure that is what do you want.
I don't know how to expand, inside call_func(), the parameters pack of params_type but, if you afford the use of a helper struct and a compiler with C++14...
I've prepared the following example with support for return type.
#include <tuple>
template<typename F>
struct function_traits;
template<typename T, typename R, typename... Args>
struct function_traits<R(T::*)(Args...) const> {
using return_type = R;
using param_types = std::tuple<Args...>;
};
template<typename T> struct function_traits : public
function_traits<decltype(&T::operator())> {};
template <typename T, typename ... Args>
T get_arg (std::tuple<Args...> const & tpl)
{ return std::get<typename std::decay<T>::type>(tpl); }
template <typename ...>
struct call_func_helper;
template <typename Func, typename Ret, typename ... Args>
struct call_func_helper<Func, Ret, std::tuple<Args...>>
{
template <typename T, typename R = Ret>
static typename std::enable_if<false == std::is_same<void, R>::value, R>::type
fn (Func const & func, T const & t)
{ return func(get_arg<Args>(t)...); }
template <typename T, typename R = Ret>
static typename std::enable_if<true == std::is_same<void, R>::value, R>::type
fn (Func const & func, T const & t)
{ func(get_arg<Args>(t)...); }
};
template <typename Func,
typename T,
typename R = typename function_traits<Func>::return_type>
R call_func (Func const & func, T const & id)
{
using param_types = typename function_traits<Func>::param_types;
return call_func_helper<Func, R, param_types>::fn(func, id);
}
int main()
{
call_func([](int const & a, char const & b) { }, std::make_tuple(3, '6'));
return 0;
}
Hope this helps.

Counting arguments of an arbitrary callable with the C++ detection idiom

I've been using the C++ detection idiom create a metafunction for determining the number of arguments to an arbitrary callable. So far, I have this (full, compilable code at http://ideone.com/BcgDhv):
static constexpr auto max_num_args = 127;
struct any { template <typename T> operator T() { } };
template <typename F, typename... Args>
using callable_archetype = decltype( declval<F>()(declval<Args>()...) );
template <typename F, typename... Args>
using is_callable_with_args = is_detected<callable_archetype, F, Args...>;
template <typename F, size_t I = 0, typename... Args>
struct count_args
: conditional<is_callable_with_args<F, Args...>::value,
integral_constant<size_t, I>,
count_args<F, I+1, Args..., any>
>::type::type
{ };
template <typename F, typename... Args>
struct count_args<F, max_num_args, Args...> : integral_constant<size_t, max_num_args> { };
This works great when none of the callable arguments are lvalue references:
void foo(int i, int j) { }
static_assert(count_args<decltype(foo)>::value == 2, "");
But when any of the arguments are lvalue references, this fails (for obvious reasons, since the callable archetype has a substitution failure):
void bar(char i, bool j, double& k);
static_assert(count_args<decltype(bar)>::value == 3, "doesn't work");
Does anyone know how to generalize this idea to make it work with lvalue references as well?
The following works (for small max_num_args):
struct any { template <typename T> operator T(); };
struct anyref { template <typename T> operator T&(); };
template <typename F, typename... Args>
using callable_archetype = decltype(std::declval<F>()(std::declval<Args>()...) );
template <typename F, typename... Args>
using is_callable_with_args = std::is_detected<callable_archetype, F, Args...>;
template <typename F, size_t I = 0, typename... Args>
struct count_args
: std::conditional<is_callable_with_args<F, Args...>::value,
std::integral_constant<std::size_t, I>,
std::integral_constant<std::size_t,
std::min(count_args<F, I+1, Args..., any>::value,
count_args<F, I+1, Args..., anyref>::value)>
>::type::type
{};
template <typename F, typename... Args>
struct count_args<F, max_num_args, Args...> :
std::integral_constant<std::size_t, max_num_args> {};
Demo
But code has to be optimized, as the complexity is 2**max_num_args :/
Change this line:
struct any { template <typename T> operator T() { } };
to:
struct any {
template <typename T> operator T&&() { }
template <typename T> operator T&() { }
};
live example
We have both an lvalue and rvalue implicit casting operator. So, we ... good?
Building off of the answer from #Jarod42, a slightly better definition of any seems to do the trick in the vast majority of cases (excluding cases that cause callable_archetype to be a substitution error for other reasons; for instance, classes with deleted copy constructors, the invocation of which wouldn't be valid anyway):
struct any {
template <typename T,
typename = enable_if_t<
not is_same<T, remove_reference_t<T>>::value
>
>
operator T();
template <typename T,
typename = enable_if_t<
is_same<T, remove_reference_t<T>>::value
>
>
operator T&();
template <typename T,
typename = enable_if_t<
is_same<T, remove_reference_t<T>>::value
>
>
operator T&&();
};
This seems to work in all of the same cases as the previous answer without the exponential scaling.
Demo

Bind to function with an unknown number of arguments in C++

Suppose I have an std::function that takes as input N arguments of type T (this can be constructed using some metaprogramming magic; see below), where N is a template parameter. I would like to std::bind the first argument to construct a function with N-1 arguments (e.g. myBind<...>(someValue)). I could not think of a clever metaprogramming trick to do this. Any suggestions?
From Lambda function with number of arguments determined at compile-time:
You can write a template n_ary_function with a nested typedef type. This type can be used as follows:
template <int N> class A {
typename n_ary_function<N, double>::type func;
};
Following the definition of n_ary_function:
template <std::size_t N, typename Type, typename ...Types>
struct n_ary_function {
using type = typename n_ary_function<N - 1, Type, Type, Types...>::type;
};
template <typename Type, typename ...Types>
struct n_ary_function<0, Type, Types...> {
using type = std::function<void(Types...)>;
};
std::bind uses std::is_placeholder to detect placeholders, which means that you can write your own placeholders to use with std::bind by partially specializing std::is_placeholder:
template<int N>
struct my_placeholder { static my_placeholder ph; };
template<int N>
my_placeholder<N> my_placeholder<N>::ph;
namespace std {
template<int N>
struct is_placeholder<::my_placeholder<N>> : std::integral_constant<int, N> { };
}
This makes it possible to get a placeholder from an integer. The rest is simply the standard integer sequence trick:
template<class R, class T, class...Types, class U, int... indices>
std::function<R (Types...)> bind_first(std::function<R (T, Types...)> f, U val, std::integer_sequence<int, indices...> /*seq*/) {
return std::bind(f, val, my_placeholder<indices+1>::ph...);
}
template<class R, class T, class...Types, class U>
std::function<R (Types...)> bind_first(std::function<R (T, Types...)> f, U val) {
return bind_first(f, val, std::make_integer_sequence<int, sizeof...(Types)>());
}
Demo. std::integer_sequence is technically C++14, but it's easily implementable in C++11 - just search on SO.
#include <functional>
#include <cstddef>
#include <utility>
#include <tuple>
template <std::size_t N, typename Type, typename... Types>
struct n_ary_function
{
using type = typename n_ary_function<N - 1, Type, Type, Types...>::type;
};
template <typename Type, typename... Types>
struct n_ary_function<0, Type, Types...>
{
using type = std::function<void(Types...)>;
};
using placeholders_list = std::tuple<decltype(std::placeholders::_1)
, decltype(std::placeholders::_2)
, decltype(std::placeholders::_3)
, decltype(std::placeholders::_4)
, decltype(std::placeholders::_5)
, decltype(std::placeholders::_6)
, decltype(std::placeholders::_7)
, decltype(std::placeholders::_8)
, decltype(std::placeholders::_9)
, decltype(std::placeholders::_10)
>;
template <typename F>
struct arity;
template <typename R, typename... Args>
struct arity<std::function<R(Args...)>>
{
static constexpr std::size_t value = sizeof...(Args);
};
template <typename F, typename T, std::size_t... Ints>
auto binder(F f, T t, std::index_sequence<Ints...>)
{
return std::bind(f, t,
typename std::tuple_element<Ints, placeholders_list>::type{}...);
}
template <typename F, typename T>
auto myBind(F f, T t)
{
return binder(f, t, std::make_index_sequence<arity<F>::value - 1>{});
}
Tests:
#include <iostream>
void foo(int a, int b, int c, int d, int e)
{
std::cout << a << b << c << d << e << std::endl;
}
int main()
{
n_ary_function<5, int>::type f = foo;
n_ary_function<4, int>::type b = myBind(f, 1);
b(2, 3, 4, 5);
}
DEMO

Initialize class containing a std::function with a lambda

I created a template class containing a std::function as a member the following way:
template<typename Ret, typename... Args>
class Foo
{
private:
std::function<Ret(Args...)> _func;
public:
Foo(const std::function<Ret(Args...)>& func):
_func(func)
{}
};
In order not to have to specify the arguments and return type of the passed function, I created some make_foo overloads:
template<typename Ret, typename... Args>
auto make_foo(Ret (&func)(Args...))
-> Foo<Ret, Args...>
{
return { std::function<Ret(Args...)>(func) };
}
template<typename Ret, typename... Args>
auto make_foo(const std::function<Ret(Args...)>& func)
-> Foo<Ret, Args...>
{
return { func };
}
However, I was unable to create a make_foo overload that takes a lambda as parameter:
template<typename Ret, typename... Args>
auto make_foo(??? func)
-> Foo<Ret, Args...>
{
return { std::function<Ret(Args...)>(func) };
}
I just can't find a way to have the return type and argument types automatically deduced from the lambda. Is there an idiomatic way to solve such a problem?
Ok, so I thought I would die, but I finally managed to do it ç_ç
First, I used the usual indices. Since I do not have the official ones, I used old indices I wrote some months ago:
template<std::size_t...>
struct indices {};
template<std::size_t N, std::size_t... Ind>
struct make_indices:
make_indices<N-1, N-1, Ind...>
{};
template<std::size_t... Ind>
struct make_indices<0, Ind...>:
indices<Ind...>
{};
Then, I used some function traits found somewhere on StackOverflow. They are nice, and I think that they are equivalent to the Boost library linked in the comments:
template<typename T>
struct function_traits:
function_traits<decltype(&T::operator())>
{};
template<typename C, typename Ret, typename... Args>
struct function_traits<Ret(C::*)(Args...) const>
{
enum { arity = sizeof...(Args) };
using result_type = Ret;
template<std::size_t N>
using arg = typename std::tuple_element<N, std::tuple<Args...>>::type;
};
Then, I was able to write a proper make_foo function and it implementation function, since both are required to use indices. Be careful, it's plain ugly:
template<typename Function, std::size_t... Ind>
auto make_foo_(Function&& func, indices<Ind...>)
-> Foo<
typename function_traits<typename std::remove_reference<Function>::type>::result_type,
typename function_traits<typename std::remove_reference<Function>::type>::template arg<Ind>...>
{
using Ret = typename function_traits<typename std::remove_reference<Function>::type>::result_type;
return { std::function<Ret(typename function_traits<typename std::remove_reference<Function>::type>::template arg<Ind>...)>(func) };
}
template<typename Function, typename Indices=make_indices<function_traits<typename std::remove_reference<Function>::type>::arity>>
auto make_foo(Function&& func)
-> decltype(make_foo_(std::forward<Function>(func), Indices()))
{
return make_foo_(std::forward<Function>(func), Indices());
}
The code is somehow ugly and unreadable, but it definitely works. Hope it does not rely on some implementation-defined behaviour now. Also, thanks all for your advice, it helped! :)
int main()
{
auto lambda = [](int i, float b, long c)
{
return long(i*10+b+c);
};
auto foo = make_foo(lambda);
std::cout << foo(5, 5.0, 2) << std::endl; // 57, it works!
}
And here is the live example :)
I have an example that works with mutable lambdas. I can't quite figure out how to get the CV member qualification right.
First, here's the function template we're after:
#include <functional>
template <typename R, typename ...Args>
void foo(std::function<R(Args...)> f)
{ }
Now we'll let a function template bar take an arbitrary lambda and call the right version of foo, by inspecting the type of the lambda's operator():
#include <type_traits>
template <typename> struct remove_member;
template <typename C, typename T>
struct remove_member<T C::*>
{ using type = T; };
template <typename F>
void bar(F f)
{
using ft = decltype(&F::operator());
foo(std::function<typename remove_member<ft>::type>(f));
}
Example:
int q;
bar([&](int a, int b) mutable -> int { q = a + b; return q / b; });
You can use normal, const lambdas with this modified trait, though I don't like having to spell the function type out:
template <typename C, typename R, typename ...Args>
struct remove_member<R (C::*)(Args...) const>
{ using type = R(Args...); };
I thought it might work with the original code if I use typename std::remove_cv<T>::type, but at least on GCC this doesn't work because of some strange __attribute__((const)) that's set on the lambda's operator type which seems to interfere with the template specialization.

trailing return type using decltype with a variadic template function

I want to write a simple adder (for giggles) that adds up every argument and returns a sum with appropriate type.
Currently, I've got this:
#include <iostream>
using namespace std;
template <class T>
T sum(const T& in)
{
return in;
}
template <class T, class... P>
auto sum(const T& t, const P&... p) -> decltype(t + sum(p...))
{
return t + sum(p...);
}
int main()
{
cout << sum(5, 10.0, 22.2) << endl;
}
On GCC 4.5.1 this seems to work just fine for 2 arguments e.g. sum(2, 5.5) returns with 7.5. However, with more arguments than this, I get errors that sum() is simply not defined yet. If I declare sum() like this however:
template <class T, class P...>
T sum(const T& t, const P&... p);
Then it works for any number of arguments, but sum(2, 5.5) would return integer 7, which is not what I would expect.
With more than two arguments I assume that decltype() would have to do some sort of recursion to be able to deduce the type of t + sum(p...). Is this legal C++0x? or does decltype() only work with non-variadic declarations? If that is the case, how would you write such a function?
I think the problem is that the variadic function template is only considered declared after you specified its return type so that sum in decltype can never refer to the variadic function template itself. But I'm not sure whether this is a GCC bug or C++0x simply doesn't allow this. My guess is that C++0x doesn't allow a "recursive" call in the ->decltype(expr) part.
As a workaround we can avoid this "recursive" call in ->decltype(expr) with a custom traits class:
#include <iostream>
#include <type_traits>
using namespace std;
template<class T> typename std::add_rvalue_reference<T>::type val();
template<class T> struct id{typedef T type;};
template<class T, class... P> struct sum_type;
template<class T> struct sum_type<T> : id<T> {};
template<class T, class U, class... P> struct sum_type<T,U,P...>
: sum_type< decltype( val<const T&>() + val<const U&>() ), P... > {};
This way, we can replace decltype in your program with typename sum_type<T,P...>::type and it will compile.
Edit: Since this actually returns decltype((a+b)+c) instead of decltype(a+(b+c)) which would be closer to how you use addition, you could replace the last specialization with this:
template<class T, class U, class... P> struct sum_type<T,U,P...>
: id<decltype(
val<T>()
+ val<typename sum_type<U,P...>::type>()
)>{};
Apparently you can't use decltype in a recursive manner (at least for the moment, maybe they'll fix it)
You can use a template structure to determine the type of the sum
It looks ugly but it works
#include <iostream>
using namespace std;
template<typename... T>
struct TypeOfSum;
template<typename T>
struct TypeOfSum<T> {
typedef T type;
};
template<typename T, typename... P>
struct TypeOfSum<T,P...> {
typedef decltype(T() + typename TypeOfSum<P...>::type()) type;
};
template <class T>
T sum(const T& in)
{
return in;
}
template <class T, class... P>
typename TypeOfSum<T,P...>::type sum(const T& t, const P&... p)
{
return t + sum(p...);
}
int main()
{
cout << sum(5, 10.0, 22.2) << endl;
}
C++14's solution:
template <class T, class... P>
decltype(auto) sum(const T& t, const P&... p){
return t + sum(p...);
}
Return type is deducted automatically.
See it in online compiler
Or even better if you want to support different types of references:
template <class T, class... P>
decltype(auto) sum(T &&t, P &&...p)
{
return std::forward<T>(t) + sum(std::forward<P>(p)...);
}
See it in online compiler
If you need a natural order of summation (that is (((a+b)+c)+d) instead of (a+(b+(c+d)))), then the solution is more complex:
template <class A>
decltype(auto) sum(A &&a)
{
return std::forward<A>(a);
}
template <class A, class B>
decltype(auto) sum(A &&a, B &&b)
{
return std::forward<A>(a) + std::forward<B>(b);
}
template <class A, class B, class... C>
decltype(auto) sum(A &&a, B &&b, C &&...c)
{
return sum( sum(std::forward<A>(a), std::forward<B>(b)), std::forward<C>(c)... );
}
See it in online compiler
Another answer to the last question with less typing by using C++11's std::common_type: Simply use
std::common_type<T, P ...>::type
as return type of your variadic sum.
Regarding std::common_type, here is an excerpt from http://en.cppreference.com/w/cpp/types/common_type:
For arithmetic types, the common type may also be viewed as the type
of the (possibly mixed-mode) arithmetic expression such as T0() + T1()
+ ... + Tn().
But obviously this works only for arithmetic expressions and doesn't cure the general problem.
I provide this improvement to the accepted answer. Just two structs
#include <utility>
template <typename P, typename... Ps>
struct sum_type {
using type = decltype(std::declval<P>() + std::declval<typename sum_type<Ps...>::type>());
};
template <typename P>
struct sum_type<P> {
using type = P;
};
Now just declare your functions as
template <class T>
auto sum(const T& in) -> T
{
return in;
}
template <class P, class ...Ps>
auto sum(const P& t, const Ps&... ps) -> typename sum_type<P, Ps...>::type
{
return t + sum(ps...);
}
With this, your test code now works
std::cout << sum(5, 10.0, 22.2, 33, 21.3, 55) << std::endl;
146.5
Right way to do:
#include <utility>
template <typename... Args>
struct sum_type;
template <typename... Args>
using sum_type_t = typename sum_type<Args...>::type;
template <typename A>
struct sum_type<A> {
using type = decltype( std::declval<A>() );
};
template <typename A, typename B>
struct sum_type<A, B> {
using type = decltype( std::declval<A>() + std::declval<B>() );
};
template <typename A, typename B, typename... Args>
struct sum_type<A, B, Args...> {
using type = sum_type_t< sum_type_t<A, B>, Args... >;
};
template <typename A>
sum_type_t<A> sum(A &&a)
{
return (std::forward<A>(a));
}
template <typename A, typename B>
sum_type_t<A, B> sum(A &&a, B &&b)
{
return (std::forward<A>(a) + std::forward<B>(b));
}
template <typename A, typename B, typename... C>
sum_type_t<A, B, C...> sum(A &&a, B &&b, C &&...args)
{
return sum( sum(std::forward<A>(a), std::forward<B>(b)), std::forward<C>(args)... );
}
https://coliru.stacked-crooked.com/a/a5a0e8019e40b8ba
This completely preserves resulting type of operations (even r-value referenceness). The order of operations is natural: (((a+b)+c)+d).
For C++17:
template <class... P>
auto sum(const P... p){
return (p + ...);
}
int main()
{
std::cout << sum(1, 3.5, 5) << std::endl;
return EXIT_SUCCESS;
}
Read about folding expressions.