为什么从C++ 11中删除了对范围访问?

HC4*_*ica 54 c++ foreach range c++11 std-pair

我刚刚发现,在某一点上,C++ 11草案具有std::begin/ std::end重载std::pair,允许将一对迭代器视为适合在基于范围的for循环中使用的范围(N3126,第20.3.5.5节),但这有自从被删除.

有谁知道为什么它被删除了?

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

  • 在基于范围的for循环中开始/结束的查找规则表示在1)中作为范围对象2)的成员函数查找开始/结束作为"关联命名空间"中的自由函数
  • std::pair 没有开始/结束成员函数
  • 通常唯一关联的命名空间std::pair<T, U>是namespace std
  • 我们不允许超载std::begin/ std::endstd::pair自己
  • 我们不能专门化std::begin/ std::endfor std::pair(因为专业化必须是部分的,而且不允许使用函数)

还有其他一些我失踪的方式吗?

sel*_*tze 41

我认为Alisdair Meredith 的2009年论文"Pairs not not good range"至少是答案的一部分.基本上,许多算法返回的迭代器对实际上并不保证是有效范围.pair<iterator,iterator>由于这个原因,似乎他们从for-range循环中删除了支持.但是,提出的解决方案尚未完全采用.

如果您确定某些迭代器实际上代表了一个有效范围,那么您可以将它们包装成一个提供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))) {
       ...
    }
}
Run Code Online (Sandbox Code Playgroud)

(另)

我同意这有点蠢.返回有效范围的函数(如equal_range)应该使用适当的返回类型.我们必须通过as_range上述方式手动确认这一点,这有点令人尴尬.

  • boost :: range确实如此.使用boost;:范围而不是手写的东西可能更好.[1]:http://www.boost.org/doc/libs/1_53_0/libs/range/doc/html/range/reference/utilities/iterator_range.html (3认同)

小智 8

你可以用boost::make_iterator_range.它用begin()end()方法构造一个iterator_range . boost::make_iterator_range可以接受std::pair迭代器.


Ric*_*ges 6

使用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;
}
Run Code Online (Sandbox Code Playgroud)

  • 这很有效,轻微的狡辩 - posix保留了后缀`_t`.请参阅:[在C++标识符中使用下划线有哪些规则?](http://stackoverflow.com/questions/228783/what-are-the-rules-about-using-an-underscore-in-ac-标识符). (3认同)
  • 老实说,POSIX声称所有标识符后缀为`_t`,请参阅[GNU - 保留名称](http://www.gnu.org/software/libc/manual/html_node/Reserved-Names.html).所以POSIX认为自己现在或在将来的任何时候都可以在全局命名空间中声明一个带有该后缀的新类型.POSIX不是C++所以新类型不会嵌套在`std`中它将是全局的.如果该类型与现有代码库中的类型冲突,那么该代码库被认为是(并且一直)错误,因为规则不能播放. (3认同)
  • 哼.在这种情况下,posix与boost库的大部分和一些相当有影响力的c ++开发人员不一致.我想如果我们想要超级安全,我们可以争辩一个类型的_t在全局命名空间以外的任何命名空间中都是允许的 - 因为那是唯一一个可能被c(因此是posix)代码污染的. (3认同)
  • 从链接的答案 - "为C语言或POSIX.1环境的未来扩展保留了一些额外的标识符类别.虽然现在使用这些名称可能不会导致问题,但它们确实增加了与未来版本的C或POSIX标准冲突,所以你应该避免这些名称." 因此,仅仅因为您将其用于某种类型并不能防止可能的未来冲突. (2认同)
  • 这个"轻微的狡辩"比预期更进一步;-).我完全同意许多开发人员采用了这个惯例.我个人避免使用后缀,例如采用你的解决方案我使用`struct range`和`make_range`来获取自由函数.但这是一个在额外谨慎方面犯错误的情况. (2认同)
  • 讨论很有用 - 我学到了一些东西:-)我非常喜欢_t后缀,因为它允许我断言名称是一种不引人注目的类型.我非常喜欢在c和c ++中使用`lower_case_names`,因为我觉得它们比`camelCaps`更容易阅读更自然.我也经常给这样的仿函数命名:`struct do_something_f;`断言它是一个函数对象. (2认同)
  • pair_t的ctor引用了右值,因此仅绑定到右值。它不是通用的/转发的引用,因为它的推论没有得出。`std :: forward`调用传递了一个非引用类型,因此无条件移动。没关系,但是您可能也想接受左值。 (2认同)