简体   繁体   中英

C++ type of dereferenced iterator

I tried to make a function that sums all elements of an std::vector :

template<typename IteratorT>
auto sum(IteratorT first, IteratorT last) -> decltype(*first) {
    decltype(*first) sum = 0;
    for (; first != last; ++first)
        sum += *first;

    return sum;
}

and I got this error:

cannot convert from 'int' to 'int&'

Then after some research I found this: std::iterator_traits<IteratorT>::difference_type . Changed my code to:

template<typename IteratorT>
auto sum(IteratorT first, IteratorT last) -> typename std::iterator_traits<IteratorT>::difference_type {
    std::iterator_traits<IteratorT>::difference_type sum = 0;
    for (; first != last; ++first)
        sum += *first;

    return sum;
}

and it did work, but I am not sure why and if it's a good solution or not. In conclusion I have two questions:

1) Why decltype(*first) returns int& and not int as I expected
2) What exactly typename before std::iterator_traits<IteratorT>::difference_type does and why sum function doesn't work if I remove it

decltype(*first) returns a reference, as else we would not be able to write things like

*first = 7;

Regarding difference_type it's not clear what you's trying to do. The word typename is required in order to tell the compiler that a so called dependent type (the usual case is any type after a :: ) is a template and not a value.

Try this:

#include <type_traits>

template <
    typename Iter,
    typename Ret = typename std::decay<decltype(*std::declval<Iter>())>::type>
Ret f(Iter first, Iter last)
{
    Ret sum {};
    for (; first != last; ++first) { sum += *first; }
    return sum;
}

There are two main problems:

  • The type of aa dereferenced iterator is a reference, it can be const , and for a std::vector it can be very different from the vector's item type.

  • When the item type is eg bool , you don't want to do the sum in bool type.

The following code is one solution:

#include <iterator>     // std::iterator_traits

template< class Iter >
auto sum( Iter first, Iter last )
    -> decltype( typename std::iterator_traits<Iter>::value_type() + 0 )
{
    decltype( typename std::iterator_traits<Iter>::value_type() + 0 ) sum = 0;
    for (; first != last; ++first)
        sum += *first;
    return sum;
}

#include <iostream>
#include <vector>
#include <utility>
using namespace std;

#define ITEMS( x ) begin( x ), end( x )

auto main()
    -> int
{
    vector<double> const v1 = {3, 1, 4, 1, 5};
    cout << sum( ITEMS(v1) ) << endl;

    vector<bool> const v2 = {0, 1, 1, 0, 1};
    cout << sum( ITEMS( v2) ) << endl;
}

Note that you don't have to define your own sum : there is std::accumulate .

You did a random thing, which works randomly. distance_type has nothing to do with your problem, it's a type which denotes the result of measuring a distance between two iterators. Since more often than not it is a integer type, you ended up being able to use it in your arithmetic.

The original issue was due to the fact that dereferencing an iterator returns a (const) pointer to the underlying object - and this is needed so that code like

*it = 10;

would do what you want it to do (or failed to compile for const iterators).

And the typename keyword is required, but there are enough duplicates here on typename usage here already.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM