Question

As an excercise, I want to create a generic templated summing function for containers. While my goal is well achieved with "nested template" overloads, I want to write a function which is based solely on std::begin()/std::end(), to allow it to work on everything that's got an overload for those functions and harness the full power of C++11 SFINAE.

I'm running into problems with the return type, however. Here's what I tried, illustraded by generic "sum" function for numeric containers:

First, usage code:

int arr[] = {1,2,3,4};  
vector<int> vec(begin(arr),end(arr));
auto sum1 = sum(vec);
auto sum2 = sum(arr);

That's my expectation of the function - to work both from containers and arrays.

Here's my attempt at solving the problem:

template<typename T> auto sum(const T& container) -> decltype(*begin(container)) {
    typedef remove_const<remove_reference<decltype(*begin(container))>::type>::type val_type;
    val_type sum;
    if (is_arithmetic<val_type>::value)
        sum = 0;
    for (const auto& value: container)
        sum = sum + value;
    return sum;
}

What's wrong with that: It works, but gives out a warning (C4172: returning address of local variable or temporary). That's obvoiusly a bad thing, spawning undefined behavior, basically.

I tried defining the trailing return type in following ways:

1.Copy declaration for var_type

template<typename T> auto sum(const T& container) ->
    remove_const<remove_reference<decltype(*begin(container))>::type>::type

This spawns an error: C2923: 'std::remove_const' : 'std::remove_reference<_Ty>::type' is not a valid template type argument for parameter '_Ty' (strange, because same works for val_type

2.Use container[0]

template<typename T> auto sum(const T& container) -> decltype(container[0])

This works, but it both spawns additional restriction on container type, which I'd like to avoid, and gives out C4172 for vector

3.Try to remove just reference

template<typename T> auto sum(const T& container) -> remove_reference<decltype(*begin(container))>::type

Result: error C2061: syntax error : identifier 'type'.

Any thoughts?

Was it helpful?

Solution

Is it simple : decltype(*std::begin(container)) is resolved in a T const &. Apply the same operations as val_type, add some typename keywords and it'll work.

Why is typename mandatory ? Look at the errors :

error:   expected a type, got ‘std::remove_reference<decltype (* std::begin(container))>::type’

If you try just adding typename before this expresssion, you now have :

error: need ‘typename’ before
    ‘std::remove_const<typename std::remove_reference<decltype (* std::begin(container))>::type>::type’
because
    ‘std::remove_const<typename std::remove_reference<decltype (* std::begin(container))>::type>’
is a dependent scope

Working example with GCC 4.7.2 :

#include <iostream>
#include <type_traits>
#include <vector>

using namespace std;

template <class T>
auto sum (T const & container)
-> typename remove_const<typename remove_reference<decltype(*begin(container))>::type>::type
{
    typedef typename remove_const<typename remove_reference<decltype(*begin(container))>::type>::type val_type;
    val_type sum;
    if (is_arithmetic<val_type>::value)
        sum = 0;
    for (const auto& value: container)
        sum = sum + value;
    return sum;
}

int main ()
{
    vector<int> v { 0, 1, 2, 3 };
    cout << sum(v) << endl;
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top