为什么要从c++ 11中删除对范围访问?

时间:2022-09-02 10:09:50

I just discovered that at one point, the C++11 draft had std::begin/std::end overloads for std::pair that allowed treating a pair of iterators as a range suitable for use in a range-based for loop (N3126, section 20.3.5.5), but this has since been removed.

我刚刚发现,在一个点上,c++ 11的草案有std::开始/std:::end overload for std::pair,允许将一对迭代器作为一个范围,适合在一个基于范围的for循环中使用(N3126,第20.3.5.5节),但是这已经被删除了。

Does anyone know why it was removed?

有人知道它为什么被移除吗?

I find the removal very unfortunate, because it seems there is no other way to treat a pair of iterators as a range. Indeed:

我发现删除是非常不幸的,因为似乎没有其他方法可以将一对迭代器视为一个范围。事实上:

  • The lookup rules for begin/end in a range-based for loop say that begin/end are looked for in 1) as member functions of the range object 2) as free functions in "associated namespaces"
  • 在一个基于范围的for循环中开始/结束的查找规则说,开始/结束是作为范围对象2的成员函数在“关联的名称空间”中作为*函数来查找的。
  • std::pair does not have begin/end member functions
  • :pair没有开始/结束成员函数。
  • The only associated namespace for std::pair<T, U> in general is namespace std
  • std:::pair 的唯一关联命名空间是命名空间std ,>
  • We are not allowed to overload std::begin/std::end for std::pair ourselves
  • 我们不允许让std::begin/std: end for std:::pair yourself
  • We cannot specialize std::begin/std::end for std::pair (because the specialization would have to be partial and that's not allowed for functions)
  • 我们不能专门化std::begin/std::end for std:::pair(因为专门化必须是部分的,函数是不允许的)

Is there some other way that I am missing?

还有什么别的方法让我怀念吗?

3 个解决方案

#1


38  

I think the 2009 paper "Pairs do not make good ranges" by Alisdair Meredith is at least part of the answer. Basically, many algorithms return pairs of iterators that are actually not guaranteed to be valid ranges. It seems they removed the support for pair<iterator,iterator> from the for-range loop for this reason. However, the proposed solution has not been fully adopted.

我认为,Alisdair Meredith在2009年发表的一篇论文“配对不太合适”至少是答案的一部分。基本上,许多算法返回的迭代器对实际上并不保证是有效范围。由于这个原因,它们似乎从for-range循环中删除了对pair 的支持。然而,所提议的解决办法尚未得到充分采纳。 ,iterator>

If you know for certain that some pair of iterators really represents a valid range then you could wrap them into a custom type which offers begin()/end() member functions:

如果您确定有一对迭代器确实表示了一个有效范围,那么您可以将它们打包成自定义类型,该类型提供begin()/end()成员函数:

template<class Iter>
struct iter_pair_range : std::pair<Iter,Iter> {
    iter_pair_range(std::pair<Iter,Iter> const& x)
    : std::pair<Iter,Iter>(x)
    {}
    Iter begin() const {return this->first;}
    Iter end()   const {return this->second;}
};

template<class Iter>
inline iter_pair_range<Iter> as_range(std::pair<Iter,Iter> const& x)
{ return iter_pair_range<Iter>(x); }

int main() {
    multimap<int,int> mm;
    ...
    for (auto& p : as_range(mm.equal_range(42))) {
       ...
    }
}

(untested)

(未测试)

I agree this is a bit of a wart. Functions which return valid ranges (like equal_range) should say so using an appropriate return type. It's a bit embarrasing that we have to manually confirm this via something like as_range above.

我同意这有点像疣。返回有效范围(如equal_range)的函数应该使用适当的返回类型这样说。让人有点尴尬的是,我们必须通过类似上面的as_range来手动确认这一点。

#2


8  

You can use boost::make_iterator_range. It constructs an iterator_range with begin() and end() methods. boost::make_iterator_range can accept std::pair of iterators.

你可以使用boost::make_iterator_range。它用begin()和end()方法构造iterator_range。make_iterator_range可以接受std::一对迭代器。

#3


6  

expanding on the above answer using c++11 optimisations:

使用c++11优化扩展上述答案:

#include <utility>

template<class Iter>
struct range_t : public std::pair<Iter, Iter> {
    using pair_t = std::pair<Iter, Iter>;
    range_t(pair_t&& src)
    : std::pair<Iter, Iter>(std::forward<pair_t>(src))
    {}

    using std::pair<Iter, Iter>::first;
    using std::pair<Iter, Iter>::second;

    Iter begin() const { return first; }
    Iter end() const { return second; }
};

template<class Iter>
range_t<Iter> range(std::pair<Iter, Iter> p) {
    return range_t<Iter>(std::move(p));
}

template<class Iter>
range_t<Iter> range(Iter i1, Iter i2) {
    return range_t<Iter>(std::make_pair(std::move(i1), std::move(i2)));
}


// TEST: 

#include <iostream>
#include <set>
using namespace std;

int main() {

    multiset<int> mySet { 6,4,5,5,5,3,3,67,8,89,7,5,45,4,3 };

    cout << "similar elements: ";
    for (const auto&i : range(mySet.lower_bound(5), mySet.upper_bound(10))) {
        cout << i << ",";
    }
    cout << "\n";

    int count = 0, sum = 0;
    for (const auto& i: range(mySet.equal_range(5)))
    {
        ++count;
        sum += i;
    }
    cout << "5 appears " << count << " times\n"
    << "the sum is " << sum << "\n";

return 0;
}

#1


38  

I think the 2009 paper "Pairs do not make good ranges" by Alisdair Meredith is at least part of the answer. Basically, many algorithms return pairs of iterators that are actually not guaranteed to be valid ranges. It seems they removed the support for pair<iterator,iterator> from the for-range loop for this reason. However, the proposed solution has not been fully adopted.

我认为,Alisdair Meredith在2009年发表的一篇论文“配对不太合适”至少是答案的一部分。基本上,许多算法返回的迭代器对实际上并不保证是有效范围。由于这个原因,它们似乎从for-range循环中删除了对pair 的支持。然而,所提议的解决办法尚未得到充分采纳。 ,iterator>

If you know for certain that some pair of iterators really represents a valid range then you could wrap them into a custom type which offers begin()/end() member functions:

如果您确定有一对迭代器确实表示了一个有效范围,那么您可以将它们打包成自定义类型,该类型提供begin()/end()成员函数:

template<class Iter>
struct iter_pair_range : std::pair<Iter,Iter> {
    iter_pair_range(std::pair<Iter,Iter> const& x)
    : std::pair<Iter,Iter>(x)
    {}
    Iter begin() const {return this->first;}
    Iter end()   const {return this->second;}
};

template<class Iter>
inline iter_pair_range<Iter> as_range(std::pair<Iter,Iter> const& x)
{ return iter_pair_range<Iter>(x); }

int main() {
    multimap<int,int> mm;
    ...
    for (auto& p : as_range(mm.equal_range(42))) {
       ...
    }
}

(untested)

(未测试)

I agree this is a bit of a wart. Functions which return valid ranges (like equal_range) should say so using an appropriate return type. It's a bit embarrasing that we have to manually confirm this via something like as_range above.

我同意这有点像疣。返回有效范围(如equal_range)的函数应该使用适当的返回类型这样说。让人有点尴尬的是,我们必须通过类似上面的as_range来手动确认这一点。

#2


8  

You can use boost::make_iterator_range. It constructs an iterator_range with begin() and end() methods. boost::make_iterator_range can accept std::pair of iterators.

你可以使用boost::make_iterator_range。它用begin()和end()方法构造iterator_range。make_iterator_range可以接受std::一对迭代器。

#3


6  

expanding on the above answer using c++11 optimisations:

使用c++11优化扩展上述答案:

#include <utility>

template<class Iter>
struct range_t : public std::pair<Iter, Iter> {
    using pair_t = std::pair<Iter, Iter>;
    range_t(pair_t&& src)
    : std::pair<Iter, Iter>(std::forward<pair_t>(src))
    {}

    using std::pair<Iter, Iter>::first;
    using std::pair<Iter, Iter>::second;

    Iter begin() const { return first; }
    Iter end() const { return second; }
};

template<class Iter>
range_t<Iter> range(std::pair<Iter, Iter> p) {
    return range_t<Iter>(std::move(p));
}

template<class Iter>
range_t<Iter> range(Iter i1, Iter i2) {
    return range_t<Iter>(std::make_pair(std::move(i1), std::move(i2)));
}


// TEST: 

#include <iostream>
#include <set>
using namespace std;

int main() {

    multiset<int> mySet { 6,4,5,5,5,3,3,67,8,89,7,5,45,4,3 };

    cout << "similar elements: ";
    for (const auto&i : range(mySet.lower_bound(5), mySet.upper_bound(10))) {
        cout << i << ",";
    }
    cout << "\n";

    int count = 0, sum = 0;
    for (const auto& i: range(mySet.equal_range(5)))
    {
        ++count;
        sum += i;
    }
    cout << "5 appears " << count << " times\n"
    << "the sum is " << sum << "\n";

return 0;
}