使用n_copy函数从一个向量复制前n个元素到另一个向量的模板函数导致编译错误。

4

我使用以下模板函数,将一个向量中的前n个元素复制到另一个向量中:


// 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;
}

问题在于我遇到了以下错误,但我不明白为什么:
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个回答

5

这段代码有两个问题。第一个问题是它需要包含#include <algorithm>,以定义std::copy_n函数。第二个问题是你需要使用begin()函数获得范围的起始位置。

修复后,代码如下所示:

// 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;
}

简化版本

我们可以利用 std::vector 的区间构造函数,编写一个更短、更有效的 take 版本:

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);  
}

1
简短版:几乎 - 范围检查仍然是必要的。我会满意于一个像 // range check as before 这样的注释,但是它不应该完全被删除而没有任何通知。 - Aconcagua

4
你已经接近成功了,只有第一个 std::copy_n 算法是错误的。请更改调用方式为:
using std::begin;

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

并且它应该按照预期工作。另外,缺少#include <algorithm>,您可以预先知道生成序列的大小,从而防止不必要的分配:

result.reserve(nbrElements); // before the call to copy_n

3
你可以尝试像这样的做法:
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;
}

编辑:

您也可以直接返回向量:

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

1
insert 不会复制最后一个元素,所以通过减去 1,实际上插入的元素比期望的少一个! - Aconcagua
@Aconcagua 是的,你说得对,我会编辑我的回答。 - Raffallo

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接