I am using the following template function in order to copy the first n elements from one vector to another.


// Example program
#include <iostream>
#include <string>
#include <vector>

template <typename Range>
inline std::vector<typename Range::value_type> take(const Range &iRange, int nbrElements) {
  std::vector<typename Range::value_type> result;
  if (nbrElements > iRange.size()) {
    nbrElements = iRange.size();
  }

  std::copy_n(iRange, nbrElements, std::back_inserter(result));

  return result;
}

int main()
{
  std::vector<int> source = { 1, 2, 3, 4, 5, 6, 7,};
  std::vector<int> destination = take(source, 7);

  return 0;
}

The problem is that I am getting the following error and I don't understand why:

In instantiation of 'std::vector<typename Range::value_type>
take(const Range&, int) [with Range = std::vector<int>; typename
Range::value_type = int]':
    22:48:   required from here
    10:19: warning: comparison between signed and unsigned integer expressions [-Wsign-compare]
    In file included from /usr/include/c++/4.9/algorithm:62:0,
                     from 5:
    /usr/include/c++/4.9/bits/stl_algo.h: In instantiation of '_OIter std::copy_n(_IIter, _Size, _OIter) [with _IIter = std::vector<int>;
_Size = int; _OIter = std::back_insert_iterator<std::vector<int> >]':
    14:62:   required from 'std::vector<typename Range::value_type> take(const Range&, int) [with Range = std::vector<int>; typename
Range::value_type = int]'
    22:48:   required from here
    /usr/include/c++/4.9/bits/stl_algo.h:804:39: error: no matching function for call to '__iterator_category(std::vector<int>&)'
           std::__iterator_category(__first));
                                           ^
    /usr/include/c++/4.9/bits/stl_algo.h:804:39: note: candidate is:
    In file included from /usr/include/c++/4.9/bits/stl_algobase.h:65:0,
                     from /usr/include/c++/4.9/bits/char_traits.h:39,
                     from /usr/include/c++/4.9/ios:40,
                     from /usr/include/c++/4.9/ostream:38,
                     from /usr/include/c++/4.9/iostream:39,
                     from 2:
    /usr/include/c++/4.9/bits/stl_iterator_base_types.h:201:5: note: template<class _Iter> typename
std::iterator_traits<_Iterator>::iterator_category
std::__iterator_category(const _Iter&)
         __iterator_category(const _Iter&)
         ^
    /usr/include/c++/4.9/bits/stl_iterator_base_types.h:201:5: note:   template argument deduction/substitution failed:
    /usr/include/c++/4.9/bits/stl_iterator_base_types.h: In substitution of 'template<class _Iter> typename
std::iterator_traits<_Iterator>::iterator_category
std::__iterator_category(const _Iter&) [with _Iter =
std::vector<int>]':
    /usr/include/c++/4.9/bits/stl_algo.h:804:39:   required from '_OIter std::copy_n(_IIter, _Size, _OIter) [with _IIter =
std::vector<int>; _Size = int; _OIter =
std::back_insert_iterator<std::vector<int> >]'
    14:62:   required from 'std::vector<typename Range::value_type> take(const Range&, int) [with Range = std::vector<int>; typename
Range::value_type = int]'
    22:48:   required from here
    /usr/include/c++/4.9/bits/stl_iterator_base_types.h:201:5: error: no type named 'iterator_category' in 'struct
std::iterator_traits<std::vector<int> >'

3 Answers

4
lubgr On Best Solutions

You are almost there, only the first std::copy_n algorithm is wrong. Change the invocation to

using std::begin;

std::copy_n(begin(iRange), nbrElements, std::back_inserter(result));

and it should work as expected. Also, #include <algorithm> is missing, and you could prevent unnecessary allocations as you know the size of the resulting sequence:

result.reserve(nbrElements); // before the call to copy_n
5
Jorge Perez On

There are two issues with the code. The first is that it needs to have #include <algorithm>, which defines std::copy_n, and the second is that you need to take the begin() of range.

When fixed, this is what the code looks like:

// Example program
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>


template <typename Range>
inline std::vector<typename Range::value_type> take(const Range &iRange, int nbrElements) {
  std::vector<typename Range::value_type> result;
  if (nbrElements > iRange.size()) {
    nbrElements = iRange.size();
  }

  std::copy_n(iRange.begin(), nbrElements, std::back_inserter(result));

  return result;
}

int main()
{
  std::vector<int> source = { 1, 2, 3, 4, 5, 6, 7,};
  std::vector<int> destination = take(source, 7);

  return 0;
}

Shorter version

We can take advantage of std::vector's range constructor to write a shorter, very efficient version of take by using std::vector's range constructor:

template <class Range, class value_t = typename Range::value_type>
std::vector<value_t> take(const Range &range, size_t count) {
  // Ensure count is at most range.size()
  count = std::min(count, range.size()); 
  return std::vector<value_t>(range.begin(), range.begin() + count);  
}
3
Community On

You could try something like that:

template <typename type>
static std::vector<type> take(const std::vector<type> &iRange, size_t nbrElements) 
{
  if (nbrElements > iRange.size()) 
  {
    nbrElements = iRange.size();
  }
  std::vector<type> result;
  result.insert(result.end(), iRange.begin(), iRange.begin() + nbrElements);
  return result;
}

Edit:

You can also return vector directly:

 return std::vector<type>(iRange.begin(), iRange.begin() + nbrElements);