Related
I am writing some template meta programming code. For some reasons, I want to make every object in my code has different type. The original code looks like this:
template<unsigned int index>
class Class1{
};
template<typename T1, typename T2, unsigned int index>
class Class2{
std::tuple<T1*, T2*> v;
public:
Class2(T1* t1, T2* t2): v(std::tuple<T1*, T2*>(t1, t2)) {}
};
template<unsigned int index>
auto makeClass1() {
return Class1<index>();
}
template<unsigned int index, typename T1, typename T2>
auto mul(T1& t1, T2& t2) {
return Class2<T1, T2, index>(&t1, &t2);
}
int main() {
auto t1 = makeClass1<0>(); // Type of TT1 is Class1<0>
auto t2 = makeClass1<1>(); // Type of TT2 is Class1<1>
auto m1 = mul<0>(t1, t2);
auto m2 = mul<1>(t1, t2); // Type of m2 is different from type of m1.
}
This code is work, but I wish my code is easy to use. So I want to ask is there any solution that can make the code look like this:
template<unsigned int index>
class Class1{
};
template<typename T1, typename T2, unsigned int index>
class Class2{
std::tuple<T1*, T2*> v;
public:
Class2(T1* t1, T2* t2): v(std::tuple<T1*, T2*>(t1, t2)) {}
};
template<unsigned int index = IncreaseCounter<?>::value>
auto makeClass1() {
return Class1<index>();
}
template<unsigned int index = IncreaseCounter<?>::value, typename T1, typename T2>
auto operator*(T1& t1, T2& t2) {
return Class2<T1, T2, index>(&t1, &t2);
}
int main() {
auto t1 = makeClass1(); // Type of TT1 is Class1<0>
auto t2 = makeClass1(); // Type of TT2 is Class1<1>
auto m1 = t1*t2
auto m2 = t1*t2; // Type of m2 is different from type of m1.
}
Note: I think I need a compile-time counter. But except the macro solution:__COUNTER__ and __LINE__ , I can't find any other compile-time solution. The macro solution is ineffective to my code.
Except the compile-time counter, any other solution is ok.
Thank you for reading my question. Due to my poor english expression ability,please bear with me for the wrong sentences.
In C++20, you might do:
template <typename = decltype([]{})>
class Class1{};
template<typename T1, typename T2, typename = decltype([]{})>
class Class2{
std::tuple<T1*, T2*> v;
public:
Class2(T1* t1, T2* t2): v(std::tuple<T1*, T2*>(t1, t2)) {}
};
template <typename T = decltype([]{})>
auto makeClass1() { return Class1<T>();}
template<typename T1, typename T2, typename T = decltype([]{})>
auto operator*(T1& t1, T2& t2) {
return Class2<T1, T2, T>(&t1, &t2);
}
int main() {
auto t1 = makeClass1();
auto t2 = makeClass1(); // Type of t2 is different from type of t1.
auto m1 = t1*t2;
auto m2 = t1*t2; // Type of m2 is different from type of m1.
static_assert(!std::is_same_v<decltype(t1), decltype(t2)>);
static_assert(!std::is_same_v<decltype(m1), decltype(m2)>);
}
Demo.
Let's regard the core of your question:
auto m1 = t1*t2;
auto m2 = t1*t2; // Type of m2 is different from type of m1.
You have exactly the same expression (t1*t2) but you wish this expression to produce two different types!
Overall your idea of counting objects in compile time is loose. What would you expect in this case?
for (int i = 0; i < 10; ++i) {
auto m = t1*t2;
}
Returning back to your code: you somehow need to introduce this compile-time index, like you do in mul<index>: either in mul or in the result type (explicit type instead of auto + appropriate conversion).
Please see code snippets (implementation of matrix multiplication) below.Is it possible to simplify them using nested pack expansion to have something like {{((a[r][k] * b[k][c]) + ...)...}...}?
#include <array>
#include <utility>
template<typename T, size_t R, size_t C>
using Matrix = std::array<std::array<T, C>, R>;
template<typename A, typename B>
using mul_el_t = decltype(std::declval<A>()[0][0] * std::declval<B>()[0][0]);
Helper to compute single element.
template<size_t R1, size_t C2, size_t... C1_R2, typename A, typename B>
auto _mat_mul_element(const A &a, const B &b, std::index_sequence<C1_R2...>)
{
return ((a[R1][C1_R2] * b[C1_R2][C2]) + ...);
}
Helper to compute particular row.
template<size_t R1, size_t... C2, typename C1_R2, typename A, typename B>
auto _mat_mul_row(const A &a, const B &b, std::index_sequence<C2...>, C1_R2 c1_r2)
-> std::array<mul_el_t<A, B>, sizeof...(C2)>
{
return {_mat_mul_element<R1, C2>(a, b, c1_r2)...};
}
This computes whole matrix using parameters packs.
template<size_t... R1, typename C2, typename C1_R2, typename A, typename B>
auto _mat_mul(const A &a, const B &b, std::index_sequence<R1...>, C2 c2, C1_R2 c1_r2)
-> Matrix<mul_el_t<A, B>, sizeof...(R1), C2::size()>
{
return {_mat_mul_row<R1>(a, b, c2, c1_r2)...};
}
And actual interface.
template<typename T, size_t R1, size_t C1_R2, size_t C2>
Matrix<T, R1, C2> operator*(const Matrix<T, R1, C1_R2> &a, const Matrix<T, C1_R2, C2> &b)
{
return _mat_mul(
a, b,
std::make_index_sequence<R1>{},
std::make_index_sequence<C2>{},
std::make_index_sequence<C1_R2>{}
);
};
UPDATE (looks like I was not clear about the actual problem I have)
When I am trying to replace _mat_mul with:
template<size_t... R1, size_t... C2, size_t... C1_R2, typename A, typename B>
auto _mat_mul(const A &a, const B &b,
std::index_sequence<R1...>,
std::index_sequence<C2...>,
std::index_sequence<C1_R2...>)
-> Matrix<mul_el_t<A, B>, sizeof...(R1), sizeof...(C2)>
{
return {{((a[R1][C1_R2] * b[C1_R2][C2]) + ...)...}...};
}
using Apple LLVM version 9.1.0 (clang-902.0.39.1) compilation fails with:
[ 50%] Building CXX object CMakeFiles/main.cpp.o
main.cpp:38:51: error: pack expansion does not contain any unexpanded parameter packs
return {{((a[R1][C1_R2] * b[C1_R2][C2]) + ...)...}...};
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^
I think the failure is expected since compiler doesn't know which pack to expand (R1, C2 or C1_R2) in each expansion block.
How can I hint the compiler in this situation (note, I can use any compiler)?
According to documentation nested pack expansion can be seen as an iterative process which starts with innermost pack expansion [3 dots]. Each pack expansion expands all parameter packs within subexpression which is contained by that pack expansion.
Thus {{((a[R1][C1_R2] * b[C1_R2][C2]) + ...)...}...} after first step becomes{{(a[0][0] * b[0][0] + a[1][1] * b[1][1])...}...} (R1/C2/C1_R2 are index_sequence<2>). So next two pack expansions just have nothing to expand.
Naive solution
It's possible to move each parameter pack to required subexpression at the same time leaving actual value it carries in the desired place. One can use analogue of FP's let ... in expression:
auto let = [](auto a, auto f) { return f(a); };
Thus original expression becomes:
{let(R1, [&](auto r1) {
return std::array<T, sizeof...(C2)>{let(C2, [&](auto c2) {
return ((a[r1][C1_R2] * b[C1_R2][c2]) + ...);
})...};
})...};
That is probably good enough, but may take some time to decipher what's going on there. Also one would still need to introduce those parameter packs in the scope.
Better solution
One could try to improve readability by abstracting over parameter pack introduction/expansion. Using following utility function.
template<typename H, typename F, typename T, T... I>
decltype(auto) repack(std::integer_sequence<T, I...>, H h, F f)
{
return h(f(std::integral_constant<T, I>{})...);
}
This function takes value which carries some pack (one could make an overload for something other than std::integer_sequence), function f which is applied to each element of the pack, and function h which is used to convert final pack to some value.
Thus full multiply routine becomes
template<typename T, size_t R1, size_t C1_R2, size_t C2>
Matrix<T, R1, C2> operator*(const Matrix<T, R1, C1_R2> &a, const Matrix<T, C1_R2, C2> &b)
{
std::make_index_sequence<R1> r1{};
std::make_index_sequence<C2> c2{};
std::make_index_sequence<C1_R2> c1_r2{};
return repack(r1, ctor<Matrix<T, R1, C2>>(), [&](auto r1) {
return repack(c2, ctor<std::array<T, C2>>(), [&](auto c2) {
return repack(c1_r2, sum, [&](auto c1_r2) {
return a[r1][c1_r2] * b[c1_r2][c2];
});
});
});
}
where ctor is
template<typename H>
auto ctor()
{
return [](auto... xs) { return H{xs...}; };
}
and sum = [](auto... xs) { return (xs +...); };.
Crazy solution
One might have spot pattern in expression with 3 nested repack's, thus multiplication routine may become:
template<typename T, size_t R1, size_t C1_R2, size_t C2>
Matrix<T, R1, C2> operator*(const Matrix<T, R1, C1_R2> &a, const Matrix<T, C1_R2, C2> &b)
{
auto item = [&](auto r1, auto c2, auto c1_r2) { return a[r1][c1_r2] * b[c1_r2][c2]; };
auto curried_repack = curry(POLY(repack));
auto m = curried_repack(std::make_index_sequence<R1>{}, ctor<Matrix<T, R1, C2>>());
auto r = curried_repack(std::make_index_sequence<C2>{}, ctor<std::array<T, C2>>());
auto e = curried_repack(std::make_index_sequence<C1_R2>{}, sum);
auto op = [](auto w, auto f) {
return compose(w, curry(f));
};
return foldr(op, m, r, e, item)();
}
With utilities:
template<typename F>
auto curry(F f)
{
return [=](auto... a) {
return [=](auto... b) { return f(a..., b...); };
};
};
template<typename F, typename G>
auto compose(F f, G g)
{
return [=](auto... xs) {
return f(g(xs...));
};
};
And macro to convert template function into value
#define POLY(f) ([](auto... a){ return f(a...); })
And foldr which is left as a homework.
Compilation note
All solutions are equivalent in sense they produce the same binary.
I have a family of classes with methods with the following signature:
double compute(list<T> pars)
This method performs a calculation with the parameters received through pars. For each compute(list) method, I have another compute(x1, x2, ..., xn) which is the method implementing the real calculation. Thus, compute(pars) should do some such as:
double compute(list<T> pars)
{
T x1 = list.pop_back();
T x2 = list.pop_back();
// .. so on until last parameter xn
T xn = list.pop_back();
return compute(x1, x2, .., xn); // here the real implementation is called
}
This pattern repeats many times, the only thing that could change is the size of pars list and of course the implementation of compute(x1, x1, ..).
I would like to find a way for "driying" this repetitive process; concretely, extracting the parameters in pars list and building the call to compute(x1, x2, .., xn). I have been trying without success to do some macro tricks.
My question is if it exists some way based on metaprogramming that allows me to implement compute(list<T> pars) once and simply reuse it n order to perform the call to compute(x1, x2, ..., xn)
EDIT: This is the signature of the other compute(x1, ...)
VtlQuantity compute(const VtlQuantity & x1,
const VtlQuantity & x2,
// any number of pars according the class
const VtlQuantity & xn) const
'VtlQuantityis a class representingdouble`'s, their units and other stuff.
You may do the following:
template <typename Func, typename T, std::size_t ... Is>
decltype(auto) apply(Func&& f, const std::list<T>& pars, std::index_sequence<Is...>)
{
std::vector<T> v(pars.rbegin(), pars.rend());
return std::forward<Func>(f)(v.at(Is)...);
}
template <std::size_t N, typename Func, typename T>
decltype(auto) apply(Func&& f, const std::list<T>& pars)
{
return apply(std::forward<Func>(f), pars, std::make_index_sequence<N>());
}
With usage similar to:
apply<6>(print, l);
Demo
To compute automatically the arity of the function, you may create a traits:
template <typename F> struct arity;
template <typename Ret, typename ...Args> struct arity<Ret(Args...)>
{
static constexpr std::size_t value = sizeof...(Args);
};
and then
template <typename Func, typename T>
decltype(auto) apply(Func&& f, const std::list<T>& pars)
{
constexpr std::size_t N = arity<std::remove_pointer_t<std::decay_t<Func>>>::value;
return apply(std::forward<Func>(f), pars, std::make_index_sequence<N>());
}
Demo
You have to enrich arity to support Functor (as the lambda).
This is a C++11 solution for the more general problem type of applying
a function or functor F, taking N type T parameters and returning type Ret, to the N arguments
at successive positions of some input iterator.
This gains several flexibilities over a solution parameterized by some container-of-T of the arguments:-
You can extract the arguments from an arbitrary N-sized range within a sequence.
The sequence need not be a container-of-T - though it must be a sequence of something convertible to T.
You can extract the arguments either last-to-first (as you do), or first-to-last,
from the standard container types or any that support forward and reverse iterators.
You may even apply F to arguments consumed directly from some input stream, without
intermediate extraction.
And of course you can change your mind about the type of sequence in which
to deliver arguments without having to change the functional-application solution.
Interface
template<typename Func, typename InIter, typename Stop = std::nullptr_t>
typename function_traits<typename std::decay<Func>::type>::return_type
invoke(Func && f, InIter it, Stop stop = Stop());
You can use this like:
auto result = invoke(func,iter);
to apply func to the arguments at N successive positions of the iterator
iter.
That way, you get no range-checking that N arguments are legitimately accessible
to your program at those positions. The range-checking code that you will spot
in the implementation will compile to nothing and if you trespass out of bounds
there will be UB.
If you want range checking you can instead code:
auto result = invoke(func,iter,end);
where end is an iterator of the same type as iter delimiting the end of the
available range in the usual manner. In this case an std::out_of_range will
be thrown if N exceeds the size of the range.
Implementation
#include <type_traits>
#include <functional>
#include <string>
template<typename T>
struct function_traits;
template <typename Ret, typename ArgT, typename... ArgRest>
struct function_traits<Ret(*)(ArgT, ArgRest...)>
{
static constexpr std::size_t n_args = 1 + sizeof...(ArgRest);
using first_arg_type = ArgT;
using return_type = Ret;
};
template <typename Ret, typename ArgT, typename... ArgRest>
struct function_traits<std::function<Ret(ArgT, ArgRest...)>>
{
static constexpr std::size_t n_args = 1 + sizeof...(ArgRest);
using first_arg_type = ArgT;
using return_type = Ret;
};
namespace detail {
template<typename Left, typename Right>
typename std::enable_if<!std::is_same<Left,Right>::value>::type
range_check(Left, Right, std::string const &){}
template<typename Left, typename Right>
typename std::enable_if<std::is_same<Left,Right>::value>::type
range_check(Left start, Right end, std::string const & gripe) {
if (start == end) {
throw std::out_of_range(gripe);
}
}
template<
std::size_t N, typename Func, typename InIter, typename Stop,
typename ...Ts
>
typename std::enable_if<
N == function_traits<typename std::decay<Func>::type>::n_args,
typename function_traits<typename std::decay<Func>::type>::return_type
>::type
invoke(Func && f, InIter, Stop, Ts...args)
{
return f(args...);
}
template<
std::size_t N, typename Func, typename InIter, typename Stop,
typename ...Ts
>
typename std::enable_if<
N != function_traits<typename std::decay<Func>::type>::n_args,
typename function_traits<typename std::decay<Func>::type>::return_type
>::type
invoke(Func && f, InIter it, Stop stop, Ts...args)
{
range_check(it,stop,
"Function takes more arguments than are available "
"in `" + std::string(__PRETTY_FUNCTION__) + '`');
using arg_type = typename
function_traits<typename std::decay<Func>::type>::first_arg_type;
auto arg = static_cast<arg_type>(*it);
return invoke<N + 1>(std::forward<Func>(f),++it,stop,args...,arg);
}
} // namespace detail
template<typename Func, typename InIter, typename Stop = std::nullptr_t>
typename function_traits<typename std::decay<Func>::type>::return_type
invoke(Func && f, InIter it, Stop stop = Stop())
{
return detail::invoke<0>(std::forward<Func>(f),it,stop);
}
The two specializations of function_traits<T> provided will restrict
compilation to functional types T that take at least one argument, which should
suffice for likely applications. Should you need to support
invocation on types taking 0 arguments then you can augment them with:
template <typename Ret>
struct function_traits<Ret(*)()>
{
static constexpr std::size_t n_args = 0;
using return_type = Ret;
};
template <typename Ret>
struct function_traits<std::function<Ret()>>
{
static constexpr std::size_t n_args = 0;
using return_type = Ret;
};
The specialization for free functions function_traits<Ret(*)(ArgT, ArgRest...)>,
is strictly a redundant convenience, since they too could be wrapped in std::function
objects, as you're obliged to do for anything fancier than a free function.
Demo
For a program that exercises the features discussed you can append:
#include <iostream>
#include <list>
#include <vector>
#include <deque>
#include <sstream>
#include <iterator>
struct num
{
double d;
explicit operator double() const {
return d;
}
};
double add4(double d0, double d1, double d2, double d3)
{
std::cout << d0 << '+' << d1 << '+' << d2 << '+' << d3 << "\n=";
return d0 + d1 + d2 + d3;
}
int multiply2(int i0, int i1)
{
std::cout << i0 << '*' << i1 << "\n=";
return i0 * i1;
}
struct S
{
int subtract3(int i0, int i1, int i2) const
{
std::cout << i0 << '-' << i1 << '-' << i2 << "\n=";
return i0 - i1 - i2;
}
int compute(std::list<int> const & li) const {
std::function<int(int,int,int)> bind = [this](int i0, int i1, int i2) {
return this->subtract3(i0,i1,i2);
};
return invoke(bind,li.begin());
}
};
int main()
{
std::vector<double> vd{1.0,2.0,3.0,4.0};
std::vector<double> vdshort{9.0};
std::list<int> li{5,6,7,8};
std::deque<num> dn{num{10.0},num{20.0},num{30.0},num{40.0}};
std::istringstream iss{std::string{"10 9 8"}};
std::istream_iterator<int> it(iss);
std::cout << invoke(add4,vd.rbegin()) << '\n';
std::cout << invoke(multiply2,li.begin()) << '\n';
std::cout << invoke(add4,dn.rbegin()) << '\n';
std::cout << invoke(multiply2,++it) << '\n';
S s;
std::cout << '=' << s.compute(li) << '\n';
try {
std::cout << invoke(add4,vdshort.begin(),vdshort.end()) << '\n';
} catch(std::out_of_range const & gripe) {
std::cout << "Oops :(\n" << gripe.what() << '\n';
}
return 0;
}
The case of:
S s;
std::cout << '=' << s.compute(li) << '\n';
is particularly pertinent to your particular problem, since here we call
S::compute(std::list<int> const & li) to apply another non-static method
of S to arguments delivered in the list li. See in the implementation
of S::compute how the use of a lambda can conveniently bind both the
calling S object and S::compute into an std::function we can
pass to invoke.
Live demo
C++17 solution below. wandbox link
(Greatly simplified thanks to Jarod42)
Assumes the number of arguments N is known at compile-time, but the list can have any size.
This calls pop_back() multiple times as shown in the example, then calls a function.
template <typename T>
struct list
{
T pop_back() { return T{}; }
};
namespace impl
{
template<typename TList, std::size_t... TIs>
auto list_to_tuple(TList& l, std::index_sequence<TIs...>)
{
using my_tuple = decltype(std::make_tuple((TIs, l.pop_back())...));
return my_tuple{((void)TIs, l.pop_back())...};
}
}
template<std::size_t TN, typename TList>
auto list_to_tuple(TList& l)
{
return impl::list_to_tuple(l, std::make_index_sequence<TN>());
}
template <std::size_t TN, typename TList, typename TF>
auto call_with_list(TList& l, TF&& f)
{
return std::experimental::apply(f, list_to_tuple<TN>(l));
}
void test_compute(int, int, int)
{
// ...
}
int main()
{
list<int> l{};
call_with_list<3>(l, test_compute);
}
How does it work?
The idea is that we "convert" a list to a tuple, specifying how many elements we want to pop from the list at compile-time using list_to_tuple<N>(list).
After getting a tuple from the list, we can use std::experimental::apply to call a function by applying the elements of the tuple as arguments: this is done by call_with_list<N>(list, func).
To create a tuple from the list, two things needs to be done:
Creating an std::tuple<T, T, T, T, ...>, where T is repeated N times.
Call list<T>::pop_back() N times, putting the items in the tuple.
To solve the first problem, decltype is used to get the type of the following variadic expansion: std::make_tuple((TIs, l.pop_back())...). The comma operator is used so that TIs, l.pop_back() evaluates to decltype(l.pop_back()).
To solve the second problem, a variadic expansion is used inside the std::initializer_list tuple constructor, which guarantees order-of-evaluation: return my_tuple{((void)TIs, l.pop_back())...};. The same comma operator "trick" described above is used here.
Can I write it in C++11?
Yes, but it will be slightly more "annoying".
std::experimental::apply is not available: look online for solutions like this one.
std::index_sequence is not available: you will have to implement your own.
template<class T> using void_t = void;
template<class T, class F, std::size_t N=0, class=void>
struct arity:arity<T, F, N+1> {};
template<class F, class T, class Indexes>
struct nary_result_of{};
template<std::size_t, class T>
using ith_T=T;
template<class F, class T, std::size_t...Is>
struct nary_result_of<F, T, std::index_sequence<Is...>>:
std::result_of<F( ith_T<Is, T> )>
{};
template<class T, class F, std::size_t N>
struct arity<T, F, N, void_t<
typename nary_result_of<F, T, std::make_index_sequence<N>>::type
>>:
std::integral_constant<std::size_t, N>
{};
arity uses one C++14 feature (index sequences, easy to write in C++11).
It takes types F and a T and tells you the least number of Ts you can pass to F to make the call valid. If no number of T qualify, it blows your template instantiation stack and your compiler complains or dies.
template<class T>
using strip = typename std::remove_reference<typename std::remove_cv<T>::type>::type;
namespace details {
template<class T, std::size_t N, class F, class R,
std::size_t...Is
>
auto compute( std::index_sequence<Is...>, F&& f, R&& r ) {
std::array<T, N> buff={{
(void(Is), r.pop_back())...
}};
return std::forward<F>(f)( buff[Is]... );
}
}
template<class F, class R,
class T=strip< decltype( *std::declval<R&>().begin() ) >
>
auto compute( F&& f, R&& r ) {
return details::compute( std::make_index_sequence<arity<F,T>{}>{}, std::forward<F>(f), std::forward<R>(r) );
}
The only thing really annoying to convert to C++11 is the auto return type on compute. I'd have to rewrite my arity.
This version should auto detect the arity of even non-function pointers, letting you call this with lambdas or std::functions or what have you.
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
Writing a general minimum function, Two questions came to my mind. The code works fine with any input type and different argument number:
namespace xyz
{
template <typename T1, typename T2>
auto min(const T1 &a, const T2 &b) -> decltype(a+b)
{
return a < b ? a : b;
}
template <typename T1, typename T2, typename ... Args>
auto min(const T1 &a, const T2 &b, Args ... args) -> decltype(a+b)
{
return min(min(a, b), args...);
}
}
int main()
{
cout << xyz::min(4, 5.8f, 3, 1.8, 3, 1.1, 9) << endl;
// ^ ^ ^
// | | |
// float double int
}
Is there a better replacement for decltype(a+b)? I thing there's a standard class which I can't remember, something like decltype(std::THE_RESULT<a,b>::type).
The returned type of that decltype(std::THE_RESULT<a,b>::type)is const & or not ?
std::common_type(c++11):
For non-specialized std::common_type, the rules for determining the
common type between every pair T1, T2 are exactly the rules for
determining the return type of the ternary conditional operator where
T1 and T2 are the types of its second and the third operands.
and
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().
Not sure about const&, but you could play with std::remove_cv and std::remove_reference (and std::is_reference to find out the answer).
In fact, here's a list of type support utilities. Knock yourself out.
After the answer and worth comments I did it as below:
template <typename T1, typename T2>
auto min(const T1 &a, const T2 &b)
-> typename std::common_type<const T1&, const T2&>::type
{
return a < b ? a : b;
}
template <typename T1, typename T2, typename ... Args>
auto min(const T1 &a, const T2 &b, const Args& ... args)
-> typename std::common_type<const T1&, const T2&, const Args& ...>::type
{
return min(min(a, b), args...);
}