¿Por qué se eliminó el acceso a rango de pares de C++11?


Acabo de descubrir que en un momento dado, el borrador de C++11 tenía std::begin/std::end sobrecargas para std::pair que permitían tratar un par de iteradores como un rango adecuado para su uso en un bucle for basado en rango (N3126, sección 20.3.5.5), pero esto se ha eliminado desde entonces.

¿Alguien sabe por qué fue removido?

Encuentro la eliminación muy desafortunada, porque parece que no hay otra manera de tratar a un par de iteradores como un rango. De hecho:

  • Las reglas de búsqueda para begin / end en a range-based for loop dice que begin / end se buscan en 1) como funciones miembro del objeto range 2) como funciones libres en "espacios de nombres asociados"
  • std::pair no tiene funciones miembro begin/end
  • El único espacio de nombres asociado para std::pair<T, U> en general es namespace std
  • No se nos permite sobrecargar std::begin/std::end para std::pair nosotros mismos
  • No podemos especializarnos std::begin/std::end para std::pair (porque la especialización tendría que ser parcial y eso no está permitido para funciones)

¿Hay alguna otra manera que me falta?

Author: Soo Wei Tan, 2011-05-29

3 answers

Creo que el documento de 2009 "Los pares no hacen buenos rangos" de Alisdair Meredith es al menos parte de la respuesta. Básicamente, muchos algoritmos devuelven pares de iteradores que en realidad no están garantizados como rangos válidos. Parece que eliminaron el soporte para pair<iterator,iterator> del bucle for-range por esta razón. Sin embargo, la solución propuesta no ha sido totalmente adoptada.

Si sabes con certeza que algún par de iteradores realmente representa un rango válido entonces podrías envuélvelas en un tipo personalizado que ofrece funciones miembro 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))) {
       ...
    }
}

(no probado)

Estoy de acuerdo en que esto es un poco de una verruga. Las funciones que devuelven rangos válidos (como equal_range) deben decirlo usando un tipo de retorno apropiado. Es un poco embarazoso que tengamos que confirmar esto manualmente a través de algo como as_range anterior.

 38
Author: sellibitze,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2011-10-14 12:22:34

Puede usar boost::make_iterator_range. Construye un iterator_range con los métodos begin() y end(). boost::make_iterator_range puede aceptar std::pair de iteradores.

 8
Author: mgsergio,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-04-22 14:46:39

Ampliando la respuesta anterior usando optimizaciones de 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;
}
 6
Author: Richard Hodges,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2014-04-22 15:08:58