【问题标题】:iterators by rvalue reference (&&) in C++C++ 中的右值引用 (&&) 迭代器
【发布时间】:2016-03-28 10:11:46
【问题描述】:

Deduce type of template type in C++ 跟进我自己的问题,有些人提到通过引用传递iterators 不是惯用的,并且在给定的用例中它不起作用:

template <typename Iter> iterate(Iter &first, Iter &last)
{
    // Do something with the iterators
}

iterate(container.begin(), container.end());  // error, binding non-const ref to rvalue!

深入研究后,我发现(至少)另外两个涵盖前者的主题:

但是对于通过右值引用 && 传递迭代器是否会(甚至)比通过值传递它们更好,似乎没有问题/答案。如

template <typename Iter> iterate(Iter &&first, Iter &&last)
{
    // Do something with the iterators
}

iterate(container.begin(), container.end());

我的代码使用右值引用编译并运行良好,因此我对此的想法。

【问题讨论】:

  • 此上下文中的右值引用称为转发引用,也称为通用引用。
  • 大多数迭代器都非常小(例如,单个指针)。通过引用传递它们没有任何(性能)​​优势,并且如果您必须适应左值和右值引用,它会使您的代码更加复杂。

标签: c++ c++11


【解决方案1】:

首先,像您一样使用单个模板参数意味着如果传递的两个迭代器不具有完全相同的类型(以及相同的值类别,因为这是一个转发参考)。

例如这段代码:

template <typename Iter> 
size_t count(Iter &&first, Iter &&last) { ...... }

// ...

std::string s("hello");
auto s_it = s.begin();
count(s_it, s.end());

实际上编译失败,因为它不知道是将Iter推导出为string::iterator&amp;还是string::iterator。如果你想传递一个 const_iterator 和一个非常量迭代器,也会出现类似的问题。


您可以通过使用两个模板参数来解决此问题。但随后我们开始遇到逻辑错误。

迭代器按值传递是惯用的,因此如果迭代器突然按引用传递,可能会让人感到惊讶(并因此引入错误)。

例如:

template <typename It1, typename It2> 
size_t count(It1 &&first, It2 &&last)
{
    size_t c = 0;

    while ( first != last )
        ++first, ++c;

    return c;
}

int main()
{
    std::string s("hello");

    auto s_it = s.begin();
    std::cout << count(s_it, s.end()) << ' ';
    std::cout << *s_it << '\n';
}

编码器希望在此处输出5 h,但实际上代码将取消引用结束迭代器,因为您通过引用传递了s_it,并且函数对其进行了修改。

当然,count 可以通过获取first 的副本来避免这种情况...但是与一开始只传递值相比,现在您浪费了时间和内存。

【讨论】:

  • 我认为后者是我在使用 && 引用时最应该关心的,因为调用者“期望”迭代器根本不会被修改。这意味着采用 && 引用并没有“根本上”的错误,但它可能是惯用错误的,并且由于违反了调用者-被调用者约定而容易出错
  • @mementum:你应该自己做,为什么要首先通过引用传递迭代器
  • “为什么不呢?”可能是答案,但是看到调用约定的含义很明显,最好不要这样做。
猜你喜欢
  • 1970-01-01
  • 2015-10-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多