While learning about template parameter packs, I'm trying to write a clever, simple function to efficiently append two or more std::vector containers together.
Below are two initial solutions.
Version 1 is elegant but buggy, as it relies on side-effects during the expansion of the parameter pack, and the order of evaluation is undefined.
Version 2 works, but relies on a helper function that requires two cases. Yuck.
Can you see if you can come up with a simpler solution?
(For efficiency, the vector data should not be copied more than once.)
#include <vector>
#include <iostream>
// Append all elements of v2 to the end of v1.
template<typename T>
void append_to_vector(std::vector<T>& v1, const std::vector<T>& v2) {
for (auto& e : v2) v1.push_back(e);
}
// Expand a template parameter pack for side effects.
template<typename... A> void ignore_all(const A&...) { }
// Version 1: Concatenate two or more std::vector<> containers into one.
// Nicely simple, but buggy as the order of evaluation is undefined.
template<typename T, typename... A>
std::vector<T> concat1(std::vector<T> v1, const A&... vr) {
// Function append_to_vector() returns void, so I enclose it in (..., 1).
ignore_all((append_to_vector(v1, vr), 1)...);
// In fact, the evaluation order is right-to-left in gcc and MSVC.
return v1;
}
// Version 2:
// It works but looks ugly.
template<typename T, typename... A>
void concat2_aux(std::vector<T>& v1, const std::vector<T>& v2) {
append_to_vector(v1, v2);
}
template<typename T, typename... A>
void concat2_aux(std::vector<T>& v1, const std::vector<T>& v2, const A&... vr) {
append_to_vector(v1, v2);
concat2_aux(v1, vr...);
}
template<typename T, typename... A>
std::vector<T> concat2(std::vector<T> v1, const A&... vr) {
concat2_aux(v1, vr...);
return v1;
}
int main() {
const std::vector<int> v1 { 1, 2, 3 };
const std::vector<int> v2 { 4 };
const std::vector<int> v3 { 5, 6 };
for (int i : concat1(v1, v2, v3)) std::cerr << " " << i;
std::cerr << "\n"; // gcc output is: 1 2 3 5 6 4
for (int i : concat2(v1, v2, v3)) std::cerr << " " << i;
std::cerr << "\n"; // gcc output is: 1 2 3 4 5 6
}
A helper type: I dislike using intfor it.
struct do_in_order { template<class T>do_in_order(T&&){}};
Add up sizes:'
template<class V>
std::size_t sum_size( std::size_t& s, V&& v ) {return s+= v.size(); }
Concat. Returns type to be ignored:
template<class V>
do_in_order concat_helper( V& lhs, V const& rhs ) { lhs.insert( lhs.end(), rhs.begin(), rhs.end() ); return {}; }
Micro optimization, and lets you concat vectors of move only types:
template<class V>
do_in_order concat_helper( V& lhs, V && rhs ) { lhs.insert( lhs.end(), std::make_move_iterator(rhs.begin()), std::make_move_iterator(rhs.end()) ); return{}; }
actual function. Above stuff should be in a details namespace:
template< typename T, typename A, typename... Vs >
std::vector<T,A> concat( std::vector<T,A> lhs, Vs&&...vs ){
std::size s=lhs.size();
do_in_order _0[]={ sum_size(s,vs)..., 0 };
lhs.reserve(s);
do_in_order _1[]={ concat_helper( lhs, std::forward<Vs>(vs) )..., 0 };
return std::move(lhs); // rvo blocked
}
apologies for any typos.
There is a related answer on concatenation of strings: https://stackoverflow.com/a/21806609/1190077 .
Adapted here, it looks like:
template<typename T, typename... A>
std::vector<T> concat_version3(std::vector<T> v1, const A&... vr) {
int unpack[] { (append_to_vector(v1, vr), 0)... };
(void(unpack));
return v1;
}
This seems to work!
However, is the evaluation order of the template parameter pack now well-defined, or is it by accident that the compiler did the right thing?
The answer by Yakk (https://stackoverflow.com/a/23439527/1190077) works well.
Here is a polished version, incorporating my improvement to do_in_order and removing the sum_size external function:
// Nice syntax to allow in-order expansion of parameter packs.
struct do_in_order {
template<typename T> do_in_order(std::initializer_list<T>&&) { }
};
namespace details {
template<typename V> void concat_helper(V& l, const V& r) {
l.insert(l.end(), r.begin(), r.end());
}
template<class V> void concat_helper(V& l, V&& r) {
l.insert(l.end(), std::make_move_iterator(r.begin()),
std::make_move_iterator(r.end()));
}
} // namespace details
template<typename T, typename... A>
std::vector<T> concat(std::vector<T> v1, A&&... vr) {
std::size_t s = v1.size();
do_in_order { s += vr.size() ... };
v1.reserve(s);
do_in_order { (details::concat_helper(v1, std::forward<A>(vr)), 0)... };
return std::move(v1); // rvo blocked
}
Related
I'm trying to find an element in a std::vector based in a member function, but unforunately I have no access to a full C++11 conformant compiler.
I kwnow I can use a functor to solve this, but I wonder if there is a "functional" way to accomplish the same result.
Below is a snippet which depicts my problem:
#include <iostream>
#include <string>
#include <functional>
#include <algorithm>
#include <vector>
struct Class {
int type_;
Class(int type): type_(type) {
}
int GetType() {
return type_;
}
};
struct Functor {
Functor(int t): t_(t) {
}
bool operator()(Class c) {
return c.GetType() == t_;
}
int t_;
};
int main() {
// It also works
std::vector<Class> v2 { Class(1), Class(2), Class(3), Class(4), Class(5) };
auto it2 = std::find_if(v2.begin(), v2.end(), Functor(4));
std::cout << (it2 != v2.end() ? "Found!" : "Not found!") << std::endl;
// It would solve, but I can't use due to compiler limitations :(
it2 = std::find_if(v2.begin(), v2.end(), [](auto& v) { return v.GetType() == 4; });
std::cout << (it2 != v2.end() ? "Found!" : "Not found!") << std::endl;
// Is there any "functional based" solution to this, using std::mem_fun, std::bind1st, etc.?
// it2 = std::find_if(v2.begin(), v2.end(), ???);
return 0;
}
if my std::vector was formed by a non-complex type, I would do something like:
std::vector<int> v1 { 1, 2, 3, 4, 5 };
auto it1 = std::find_if(v1.begin(), v1.end(), std::bind1st(std::equal_to<int>(), 4));
std::cout << (it1 != v1.end() ? "Found!" : "Not found!") << std::endl;
Is there any solution do write a code similar to that above?
Edit:
I'm using GCC 4.4.1
Edit2:
Based in some comments and in the response from #scohe001, I would solve the problem overloading the global == operator.
But my curiosity isn't satisfied yet :)
Is there no way to achieve my goal using the std toolset from <funtional>?
Edit3:
Only to clarify: After reading the responses and comments, I know that it's possible to solve the simple example I posted before using the overloading of the operator==(int) and also know that I can use a function object (functor) to do the same job of the lambda expression. But, my real question is: Using ONLY the toolset available in <functional> (std::mem_fun, std::bind1st, std::equal_to, etc), can I "mimic" the behavior of the lambda/functor? If so, how can I "chain" the funtion calls to do it?
Edit4:
Apparently there's no way to solve my problem ONLY using the existing toolset from <functional>, so I'm accepting the #Caleth's response, once it's the one which is closer to what I was trying to do.
You'd have to write a bind_both adaptor yourself
it2 = std::find_if(v2.begin(), v2.end(), bind_both(std::equal_to<int>(), std::mem_fn_ref(&Class::getType), 4));
And it would have a combinatorial explosion of possibilities
template <typename Binary, typename Left, typename Arg>
class bind_left_t : public std::unary_function<Arg, typename Binary::result_type> {
Binary b;
Left l;
typename Binary::second_argument_type r;
public:
bind_left_t(Binary b, Left l, typename Binary::second_argument_type r) : b(b), l(l), r(r) {}
typename Binary::result_type operator()( Arg & arg) const { return b(l(arg), r); }
typename Binary::result_type operator()(const Arg & arg) const { return b(l(arg), r); }
};
template <typename Binary, typename Right, typename Arg>
class bind_right_t : public std::unary_function<Arg, typename Binary::result_type> {
Binary b;
typename Binary::first_argument_type l;
Right r;
public:
bind_right_t(Binary b, typename Binary::first_argument_type l, Right r) : b(b), l(l), r(r) {}
typename Binary::result_type operator()( Arg & arg) const { return b(l, r(arg)); }
typename Binary::result_type operator()(const Arg & arg) const { return b(l, r(arg)); }
};
template <typename Binary, typename Left, typename Right, typename Arg1, typename Arg2>
class bind_both_t : public std::binary_function<Arg1, Arg2, typename Binary::result_type> {
Binary b;
Left l;
Right r;
public:
bind_both_t (Binary b, Left l, Right r) : b(b), l(l), r(r) {}
typename Binary::result_type operator()( Arg1 & arg1, Arg2 & arg2) const { return b(l(arg1), r(arg2)); }
typename Binary::result_type operator()(const Arg1 & arg1, Arg2 & arg2) const { return b(l(arg1), r(arg2)); }
typename Binary::result_type operator()( Arg1 & arg1, const Arg2 & arg2) const { return b(l(arg1), r(arg2)); }
typename Binary::result_type operator()(const Arg1 & arg1, const Arg2 & arg2) const { return b(l(arg1), r(arg2)); }
};
The extra template arguments (Arg, Arg1 and Arg2) disambiguate between the three forms when calling bind_both
template <typename Binary, typename Left>
bind_left_t<Binary, Left, typename Left::argument_type> bind_both(Binary b, Left l, typename Binary::second_argument_type r)
{
return bind_left_t<Binary, Left, typename Left::argument_type>(b, l, r);
}
template <typename Binary, typename Right>
bind_right_t<Binary, Right, typename Right::argument_type> bind_both(Binary b, typename Binary::first_argument_type l, Right r)
{
return bind_right_t<Binary, Right, typename Right::argument_type>(b, l, r);
}
template <typename Binary, typename Left, typename Right>
bind_both_t<Binary, Left, Right, typename Left::argument_type, typename Right::argument_type> bind_both(Binary b, Left l, Right r)
{
return bind_both_t<Binary, Left, Right, typename Left::argument_type, typename Right::argument_type>(b, l, r);
}
It sounds like you can't modify the struct definition itself, so create an overload for operator== in the global scope:
bool operator==(const Class &lhs, const Class &rhs) {
return lhs.type_ == rhs.type_;
}
Then you can use regular old find:
std::find(v2.begin(), v2.end(), Class(4));
I'd like to write a helper function like:
template <typename F, typename Range1, typename Range2>
auto helper(const Range1& left, const Range2& right, F&& pred)
{
using namespace std; // for cbegin/cend and ADL
return pred(cbegin(left), cend(left), cbegin(right), cend(right));
}
It works well for containers:
std::vector<int> v1 = {1,2,3,4,5,6};
std::vector<int> v2 = {5,4,3,2,1,6};
std::cout << helper(v1, v2, [](const auto&... args){ return std::is_permutation(args...);}) << std::endl;
but it fails to deduce initializer_list-s (example):
std::cout << helper({1,2,3,4,5,6}, {5,4,3,2,1,6}, [](const auto&... args){ return std::is_permutation(args...);}) << std::endl;
Is there an idiomatic way to rewrite helper so that it deduces both containers and initializer_list-s?
I can't come up with anything better than overloads for all combinations of container and initializer_list.
I think the fundamental problem here is that a braced-init-list like { 1, 2, 3 } is just an initializer and not an object of type std::initializer_list<T>. It can potentially be used to initialize an object of some given type. But it's not an object of any type itself. And there doesn't seem to be anything in the rules for function template argument deduction that would allow you to get an std::initializer_list<T> from a braced-init-list argument unless your function parameter was already declared to be some sort of std::initializer_list<T> to begin with.
So I'm afraid writing those overloads will be the simplest solution…
Here is the best I can do:
template<class X>
struct Range {
X* container;
Range(X& x):container(std::addressof(x)) {}
Range(X&& x):container(std::addressof(x)) {} // dangerous, but hey
auto begin() const { using std::begin; return begin(*container); }
auto end() const { using std::end; return end(*container); }
auto cbegin() const { using std::cbegin; return cbegin(*container); }
auto cend() const { using std::cend; return cend(*container); }
};
template<class T>
struct Range<std::initializer_list<T>> {
using X=std::initializer_list<T>;
X container;
Range(X x):container(x) {}
auto begin() const { using std::begin; return begin(container); }
auto end() const { using std::end; return end(container); }
auto cbegin() const { using std::cbegin; return cbegin(container); }
auto cend() const { using std::cend; return cend(container); }
};
template<class T>
Range( std::initializer_list<T> ) -> Range<std::initializer_list< T >>;
template<class C1, class C2>
void foo( Range<C1> r1, Range<C2> c2 ) {}
test code:
Range r = {{'a', 'b', 'c'}};
(void)r;
std::vector v = {1,2,3};
foo( Range{{'a','b','c'}}, Range{v} );
you have to cast the arguments to Range manually for this to work at the call site, because class template arguement deduction doesn't work on function arguments.
We might be able to attack it differently.
template <typename F, typename Range1, typename Range2>
auto helper(const Range1& left, const Range2& right, F&& pred)
change the above syntax to a chained-call.
helper(v1)({1,2,3})[pred];
that reduces the 2^n explosion into 2. Not much of a help with 2 overloads, but still...
template<class...Ts>
struct helper_t {
std::tuple<Ts&&...> containers;
template<class T>
helper_t<T, Ts...> operator()(T&& t)&& {
return { /* append-move containers and t into one tuple */ };
}
template<class T>
helper_t<std::initializer_list<T>, Ts...> operator()(std::initializer_list<T> t)&& {
return { /* append-move containers and t into one tuple */ };
}
template<class F>
decltype(auto) operator[](F&& f)&& {
return std::move(*this).apply_impl(
std::make_index_sequence<sizeof...(Ts)>{},
std::forward<F>(f)
);
}
private:
template<std::size_t...Is, class F>
decltype(auto) apply_impl( std::index_sequence<Is...>, F&& f ) && {
using std::cbegin; using std::cend;
using std::get;
return std::forward<F>(f)(
cbegin( get<Is>(std::move(containers)) ), cend( get<Is>(std::move(containers)) )
);
}
};
static constexpr const helper_t<> helper;
I left appending the tuples as an exercise.
helper( container1 )( {1,2,3} )( container2 )[ some_lambda ];
is the syntax.
Please explicitly specify that the arguments you are passing is the initializer_list like this:-
std::cout << helper(v1, std::initializer_list<int>{5,4,3,2,1,6}, [](const auto&... args){ return std::is_permutation(args...);}) << std::endl;
std::cout << helper(std::initializer_list<int>{1,2,3,4,5,6}, std::initializer_list<int>{5,4,3,2,1,6}, [](const auto&... args){ return std::is_permutation(args...);}) << std::endl;
This is the way you can pass the initializer_list else you need to overload for all combinations of container and initializer_list
As explained by Michael Kenzel, the problem is that a braced-init-list isn't an std::intializer_list.
So I'm agree with Michael (+1): I don't see a way to write a single template function that can deduce both STL containers and (as std::initilizer_list<T>) braced-init-lists.
But if you can accept to add an helper() helper function for your helper() function, you can use the fact that a braced-init-list can be deduced as a C-style array.
So you can write a helper() specific version for C-style arrays that can convert the C-style arrays in std::initializer_list (or std::array, or... see you) or, in my example, you can simply call your original helper() function passing the arrays but explicating the types.
I mean: you can add this helper() helper function
template <typename F, typename R1, std::size_t S1,
typename R2, std::size_t S2>
auto helper (R1 const (&r1)[S1], R2 const (&r2)[S2], F && pred)
{ return helper<F, R1[S1], R2[S2]>(r1, r2, std::forward<F>(pred)); }
Drawback: this works if both ranges are STL containers or if both ranges are C-style arrays (or braced-init-lists); if you can have a STL container and a C-style array, you have to write other two helper() helper function: one for (only) the first range and one for (only) the second.
The following is a full working example
#include <vector>
#include <iostream>
#include <algorithm>
template <typename F, typename Range1, typename Range2>
auto helper (Range1 const & left, Range2 const & right, F && pred)
{ return pred(std::cbegin(left), std::cend(left),
std::cbegin(right), std::cend(right)); }
template <typename F, typename R1, std::size_t S1,
typename R2, std::size_t S2>
auto helper (R1 const (&r1)[S1], R2 const (&r2)[S2], F && pred)
{ return helper<F, R1[S1], R2[S2]>(r1, r2, std::forward<F>(pred)); }
int main ()
{
std::vector<int> v1 = {1,2,3,4,5,6};
std::vector<int> v2 = {5,4,3,2,1,6};
std::cout << helper(v1, v2,
[](auto const & ... args){ return std::is_permutation(args...);})
<< std::endl;
std::cout << helper({1, 2, 3, 4, 5, 6}, {5, 4, 3, 2, 1, 6},
[](auto const &... args){ return std::is_permutation(args...);})
<< std::endl;
}
Trying to learn some of the new features of c++17 came across fold expression. http://en.cppreference.com/w/cpp/language/fold
Using the fold expression I am able to do multiple push_back of elements .I was just trying to use the fold expression to merge multiple vector into a single vector .I know there are other ways to do merge the vector , but want to do it using fold expression
#include <iostream>
#include <vector>
template<typename T, typename... Args>
void push_back_vec(std::vector<T>& v, Args&&... args)
{
(v.push_back(args), ...);
}
int main()
{
std::vector<int> v;
std::vector<int> m ={1,2,3};
std::vector<int> x ={4,5,6};
std::vector<int> y ={7,8,9};
//push_back_vec(v,m,x,y);
push_back_vec(v, 66, 67, 68);
for (int i : v) std::cout << i << ' ';
}
Any suggestion will be helpful
Output
66 67 68 Program ended with exit code: 0
Trying to make below statement working which adds m,x,y vectors to v
push_back_vec(v,m,x,y);
Presently getting error for below line "No matching argument" //which is expected
push_back_vec(v,m,x,y);
what needs to be changed here
void push_back_vec(std::vector<T>& v, Args&&... args)
To concatenate 2 vectors, you may do
template <typename T>
void concatenate(std::vector<T>& v, const std::vector<T>& v2)
{
v.insert(v.end(), v2.begin(), v2.end());
}
so to concatenate N vectors, you may do
template <typename T, typename ... Ts>
void concatenate(std::vector<T>& v, const Ts&... ts)
{
(v.insert(v.end(), ts.begin(), ts.end()), ...);
}
If you want the same function to append value or vector, you may add several overloads:
template <typename T>
void append(std::vector<T>& v, const std::vector<T>& v2)
{
v.insert(v.end(), v2.begin(), v2.end());
}
template <typename T>
void append(std::vector<T>& v, const T& value)
{
v.push_back(value);
}
and then
template<typename T, typename... Args>
void push_back_vec(std::vector<T>& v, Args&&... args)
{
(append(v, args), ...);
}
Demo
Suppose I have a std::vector<std::vector<double>> d and want to assign it to a std::vector<std::vector<int>> i; the best I could come up with was:
#include <vector>
#include <algorithm>
using namespace std;
int main() {
vector<vector<double>> d = { {1.0, 2.0}, {3.0, 4.0} };
vector<vector<int>> i;
for_each(begin(d), end(d), [&i](vector<double> &x) {
i.emplace_back(begin(x), end(x));
}
);
return 0;
}
If both vectors were using the same type internally, I could just use the assignment operator (see C++ copying multidimensional vector):
i = d;
If the vectors were storing different types internally, but one-dimensional, I could do:
i.assign(begin(d), end(d));
Both of those are really obvious in their intention, which I don't feel is the case with my solution for the multi-dimensional approach. Is there a better way, or an accepted idiom, to do this?
It seems to me that your solution for the 2D vector is a good one.
The problem arises when you have to copy a N-dimension vectors of vectors of vectors...
Suppose you want a function copy_multi_vec() that works in a case as follows
std::vector<std::vector<std::vector<double>>> vvvd
{ { {1.0, 2.0, 3.0}, { 4.0, 5.0, 6.0} },
{ {7.0, 8.0, 9.0}, {10.0, 11.0, 12.0} } };
std::vector<std::vector<std::vector<int>>> vvvi;
copy_multi_vec(vvvi, vvvd);
In this case you can use partial template specialization in an helper class; by example
template <typename T1, typename T2>
struct cmvH
{ static void func (T1 & v1, T2 const & v2) { v1 = v2; } };
template <typename T1, typename T2>
struct cmvH<std::vector<T1>, std::vector<T2>>
{
static void func (std::vector<T1> & v1, std::vector<T2> const & v2)
{
v1.resize( v2.size() );
std::size_t i { 0U };
for ( auto const & e2 : v2 )
cmvH<T1, T2>::func(v1[i++], e2);
}
};
template <typename T1, typename T2>
void copy_multi_vec (T1 & v1, T2 const & v2)
{ cmvH<T1, T2>::func(v1, v2); }
or, if you want use the assign() method for the last level, you can define the helper struct as follows
template <typename, typename>
struct cmvH;
template <typename T1, typename T2>
struct cmvH<std::vector<T1>, std::vector<T2>>
{
static void func (std::vector<T1> & v1, std::vector<T2> const & v2)
{
v1.resize( v2.size() );
v1.assign( v2.cbegin(), v2.cend() );
}
};
template <typename T1, typename T2>
struct cmvH<std::vector<std::vector<T1>>, std::vector<std::vector<T2>>>
{
static void func (std::vector<std::vector<T1>> & v1,
std::vector<std::vector<T2>> const & v2)
{
v1.resize( v2.size() );
std::size_t i { 0U };
for ( auto const & e2 : v2 )
cmvH0<std::vector<T1>, std::vector<T2>>::func(v1[i++], e2);
}
};
Is there a better way, or an accepted idiom, to do this?
There is no getting away from assigning one element of the array at a time. The best you can do is create a function to help with it.
For example, you could use:
template <typename T1, typename T2>
void vector_copy(std::vector<std::vector<T1>>& dest,
std::vector<std::vector<T2>> const& src)
{
// Add the code to do the copy
}
and then, use
vector_copy(d, i);
I have the following sumhelper written:
template <typename T1, typename T2>
auto sum(const T1& v1, const T2& v2) -> decltype( v1 + v2) {
return v1 + v2;
}
template <typename T1, typename T2, typename... Ts>
auto sum(const T1& v1, const T2& v2, const Ts&... rest) -> decltype( v1 + v2 + sum(rest...) ) {
return v1 + v2 + sum(rest... );
}
Here is the CPP file
#include <iostream>
#include <type_traits>
#include "Sum.hpp"
struct A {
int x;
A(const int a) : x(a) { std::cout<<x<<std::endl; };
A &operator+(const A &tmp) const {
std::cout<<" + "<<tmp.x<<") ";
};
};
int main () {
std::cout<<"sum of 1,2,3,4 is : ";
auto ans = sum(1,2.2,3,4);
A a(1);
A b(2);
A c(3);
std::cout<<a.x;
a+b+c;
sum(a,b,c); //Here is syntax error
std::cout<<ans<<std::endl;
return 0;
}
Why am I not able to do the sum(a,b,c) ? When I have a+b+c working as demonstrate.
It gives a compile error when I pass objects but not when I pass primitive types
I am not able to understand the error template argument deduction/substitution failed.. how?
Here is a variation of a variardic apply_binop that takes an arbitrary operation as the first argument, and a sum wrapper that passes a binary add to it. apply_binop is better (and more clearly) known as foldr I believe:
#include <utility>
#include <iostream>
#define RETURNS(x) ->decltype(x) { return (x); }
struct add {
template<typename T, typename U>
auto operator()( T&& t, U&& u ) const
RETURNS( std::forward<T>(t)+std::forward<U>(u) )
};
template<typename Op, typename T0>
auto apply_binop( Op&& op, T0&& t0 )
RETURNS(std::forward<T0>(t0))
template<typename Op, typename T0, typename T1, typename... Ts>
auto apply_binop( Op&& op, T0&& t0, T1&& t1, Ts&&... ts )
RETURNS(
op(
std::forward<T0>(t0),
apply_binop(op, std::forward<T1>(t1), std::forward<Ts>(ts)...)
)
)
template<typename... Ts>
auto sum( Ts&&... ts )
RETURNS( apply_binop( add(), std::forward<Ts>(ts)... ) )
int main() {
std::cout << sum(1,2,3,4,5) << "\n";
std::cout << sum(1) << "\n";
std::cout << sum(1,2) << "\n";
std::cout << sum(1,2,3) << "\n";
std::cout << sum(1,2,3,4) << "\n";
std::cout << sum(1,2,3,4.7723) << "\n";
}
It is foldr because it applies the binary operation to the rightmost two, then takes that result and applies it with the 3rd last, etc. foldl does the same starting from the left.
The macro RETURNS makes up for the inability for C++ to deduce return types for single line functions (which I believe will be fixed in C++17). Getting gcc 4.7.2 to accept the above with only two apply_binop overrides took a bit of tweaking.
Implementing foldl without 3 or more overrides is a tad trickier.
Here is another answer where they discuss better ways to work around this issue:
How to implement folding with variadic templates
This is my bad (from previous answer) you should add one more template specialization at the top for a single element. Otherwise sum(1,2,3) will not find a match because the first two args will be matched by the variadic one and the other expects two args. There is only one left.
template <typename T>
T sum(const T& v) {
return v;
}
Here again another problem is your operator+ flows out with out returning anything. Which is Undefined Behavior. And if you define it as a member it should be const.
struct A {
int x;
A(const int a) : x(a) { std::cout<<x<<std::endl; };
A operator+(const A &a1) const
{
return A(a1.x + x);
}
};
So your complete program should now look like this
template <typename T>
T sum(const T& v) {
return v;
}
template <typename T1, typename T2>
auto sum(const T1& v1, const T2& v2) -> decltype( v1 + v2) {
return v1 + v2;
}
template <typename T1, typename T2, typename... Ts>
auto sum(const T1& v1, const T2& v2, const Ts&... rest) -> decltype( v1 + v2 + sum(rest...) ) {
return v1 + v2 + sum(rest... );
}
struct A {
int x;
A(const int a) : x(a) { };
A operator+(const A &a1) const { return A(a1.x + x); }
};
int main () {
std::cout<<"sum of 1,2.2,3,4 is : ";
auto ans = sum(1,2.2,3,4);
cout << ans;
A a(1); A b(2); A c(3);
a+b+c;
auto ans2 = sum(a,b,c);
std::cout<<std::endl<<"sum of A(1),A(2),A(3) is : ";
std::cout<<ans2.x<<std::endl;
return 0;
}
stdout
sum of 1,2.2,3,4 is : 10.2
sum of A(1),A(2),A(3) is : 6
Here is a correct variadic sum
#include <iostream>
namespace cs540 {
template <typename T>
const T & sum(const T & v) {
return v;
}
template <typename T, typename T2, typename ... Ts>
T sum(const T & v, const T2 & w, const Ts & ... params) {
return sum(w+v,params...);
}
}
int main() {
using namespace cs540;
using namespace std;
cout << sum(1.1,2,3,4,6,8,9,1.1) << endl;
}
You also need to mark your method operator+ as const