1

I have quickly wrote a reduce function with template meta-programming. I know it is not perfect, I should check the types to be compatible, the return type...

#include <iostream>
#include <functional>

template <typename Functor, typename T>
T reduce(Functor f, T v) {
  return v;
}

template<typename Functor, typename T1, typename... Ts>
T1 reduce(Functor f, T1 t1, Ts... ts) {
  return f(t1, reduce(f, ts...));
}

int main() {
  std::cout << reduce(std::plus<int>(), 1, 2, 3, 4, 5, 6, 7) << std::endl;
  return 0;
}

the point is that I would like to write things like

std::cout << reduce(std::plus<int>(), {1, 2, 3, 4, 5, 6, 7}) << std::endl;

or if possible

const int input[3] = {1, 2, 3};
std::cout << reduce(std::plus<int>(), input) << std::endl;
3
  • 1
    what is the question? just add an overload takes std::initializer_list<T>? Commented Mar 17, 2016 at 23:58
  • 1
    Why not use std::accumulate? Commented Mar 18, 2016 at 0:04
  • @Cheersandhth.-Alf: compile-time Commented Mar 18, 2016 at 0:41

2 Answers 2

1

You do not need a variadic template:

#include <algorithm>
#include <iostream>
#include <iterator>

namespace Detail {
    template <typename Functor, typename List>
    typename Functor::result_type reduce(Functor f, const List& list) {
        using std::begin;
        using std::end;
        typename Functor::result_type result{};
        auto pos = begin(list);
        if(pos != end(list)) {
            result = *pos;
            while(++pos != end(list))
                result = f(result, *pos);
        }
        return result;
    }
} // namespace Detail

template <typename Functor, typename List>
typename Functor::result_type reduce(Functor f, const List& list) {
    return Detail::reduce(f, list);
}

template <typename Functor, typename T>
typename Functor::result_type reduce(Functor f, std::initializer_list<T> list) {
    return Detail::reduce(f, list);
}

int main () {
    std::cout << reduce(std::plus<int>(), {1, 2, 3, 4, 5, 6, 7}) << std::endl;
    const int input[3] = {1, 2, 3};
    std::cout << reduce(std::plus<int>(), input) << std::endl;
}

A C++11 (g++ 4.8.4) variation using 'constexpr':

#include <algorithm>
#include <iostream>
#include <iterator>

// constexpr plus is C++14
template<typename T>
struct plus : public std::binary_function<T, T, T>
{
  constexpr T operator()(const T& x, const T& y) const { return x + y; }
};

// constexpr begin is C++14
template<class T, std::size_t N>
inline constexpr T* begin(T (&array)[N]) { return array; }

// constexpr end is C++14
template<class T, std::size_t N>
inline constexpr T* end(T (&array)[N]) { return array + N; }

template <typename Functor, typename Iterator>
inline constexpr typename Functor::result_type
reduce(
    const Functor& f,
    const typename Functor::result_type& value,
    Iterator first, 
    Iterator last)
{
    return (first != last)
        ? reduce(f, f(value, *first), first + 1, last)
        : value;
}

template <typename Functor, typename T, std::size_t N>
constexpr typename Functor::result_type reduce(const Functor& f, T (&array)[N]) {
    // constexpr begin/end is C++14
    // using std::begin;
    // using std::end;
    return reduce(f, typename Functor::result_type{}, begin(array), end(array));
}

template <typename Functor, typename T>
inline constexpr typename Functor::result_type reduce(
    const Functor& f,
    std::initializer_list<T> list)
{
    return reduce(f, typename Functor::result_type{}, list.begin(), list.end());
}

int main () {
    static constexpr int input[3] = {1, 2, 3};
    static_assert(28 == reduce(plus<int>(), {1, 2, 3, 4, 5, 6, 7}), "Failure");
    static_assert(6 == reduce(plus<int>(), input), "Failure");
}
Sign up to request clarification or add additional context in comments.

4 Comments

Where Detail::reduce would be a little more generic with adl enabled begin and end.
@Pixelchemist I do not get that, please clarify?
I need to evaluate it at compile-time, as in the original example
First drag in the std overloads by using std::begin; using std::end; and then use something like auto pos = begin(list); auto lisend = end(list); and then if (pos != lisend) and while(++pos != lisend).
1

http://ideone.com/nM8HH3

#include <iostream>
#include <functional>
#include <initializer_list>

template<typename Functor, typename T, size_t N>
T constexpr reduce(Functor f, T(&arr)[N]) {
    typename std::remove_const<T>::type val = arr[0];
    for(auto i = std::begin(arr)+1; i != std::end(arr); ++i) val = f(val, *i);
    return val; 
}

template<typename Functor, typename T>
T constexpr reduce(Functor f, std::initializer_list<T> il) {
    T val = *il.begin();
    for(auto i = il.begin()+1; i != il.end(); ++i) val = f(val, *i);
    return val;
}

template<typename Functor, typename T, typename... Ts>
T constexpr reduce(Functor f, T t1, Ts... ts) {
  return f(t1, reduce(f, std::initializer_list<T>({ts...})));
}

template<int value>
void print_constexpr() { std::cout << value << std::endl; }

int main() {
    // run-time or compile-time
    std::cout << reduce(std::plus<int>(), 1, 2, 3, 4, 5, 6, 7) << std::endl;  // 28
    std::cout << reduce(std::plus<int>(), {1, 2, 3, 4, 5, 6, 7}) << std::endl;// 28
    const int input[3] = {1, 2, 3};   // 6
    std::cout << reduce(std::plus<int>(), input) << std::endl;

    // compile-time
    print_constexpr< reduce(std::plus<int>(), 1, 2, 3, 4, 5, 6, 7) >();     // 28 
    print_constexpr< reduce(std::plus<int>(), {1, 2, 3, 4, 5, 6, 7}) >();   // 28 
    constexpr int input_constexpr[3] = {1, 2, 3};   // 6
    print_constexpr< reduce(std::plus<int>(), input_constexpr) >(); // 6 

    return 0;
}

Output:

28
28
6
28
28
6

You can use at compile-time any of:

  • reduce(std::plus<int>(), 1, 2, 3, 4, 5, 6, 7)
  • reduce(std::plus<int>(), {1, 2, 3, 4, 5, 6, 7})
  • reduce(std::plus<int>(), input)

With any constexpr-functors.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.