programacion - struct en una clase c++




¿Por qué utilizar funciones de inicio y fin que no son miembros en C++ 11? (4)

Cada contenedor estándar tiene un método de begin y end para devolver iteradores para ese contenedor. Sin embargo, C ++ 11 aparentemente ha introducido funciones gratuitas llamadas std::begin y std::end que llaman a las funciones de miembro de begin y end . Entonces, en lugar de escribir

auto i = v.begin();
auto e = v.end();

tu escribirías

using std::begin;
using std::end;
auto i = begin(v);
auto e = end(v);

En su charla, Writing Modern C ++ , Herb Sutter dice que siempre debe usar las funciones gratuitas ahora cuando desee el iterador de inicio o final de un contenedor. Sin embargo, él no entra en detalles en cuanto a por qué querrías. Al mirar el código, le ahorra a todos un personaje. Entonces, en lo que respecta a los contenedores estándar, las funciones gratuitas parecen ser completamente inútiles. Herb Sutter indicó que había beneficios para los contenedores no estándar, pero una vez más, no entró en detalles.

Entonces, la pregunta es: ¿qué hacen exactamente las versiones de funciones libres de std::begin y std::end más allá de llamar a sus correspondientes versiones de funciones miembro, y por qué querrían usarlas?


¿Cómo llamas a .begin() y .end() en una matriz C?

Las funciones gratuitas permiten una programación más genérica porque se pueden agregar después, en una estructura de datos que no puede modificar.


Considere el caso cuando tiene una biblioteca que contiene clase:

class SpecialArray;

tiene 2 métodos:

int SpecialArray::arraySize();
int SpecialArray::valueAt(int);

para iterar sobre sus valores que necesita heredar de esta clase y definir los métodos begin() y end() para casos en los que

auto i = v.begin();
auto e = v.end();

Pero si siempre usas

auto i = begin(v);
auto e = end(v);

Puedes hacerlo:

template <>
SpecialArrayIterator begin(SpecialArray & arr)
{
  return SpecialArrayIterator(&arr, 0);
}

template <>
SpecialArrayIterator end(SpecialArray & arr)
{
  return SpecialArrayIterator(&arr, arr.arraySize());
}

donde SpecialArrayIterator es algo así como:

class SpecialArrayIterator
{
   SpecialArrayIterator(SpecialArray * p, int i)
    :index(i), parray(p)
   {
   }
   SpecialArrayIterator operator ++();
   SpecialArrayIterator operator --();
   SpecialArrayIterator operator ++(int);
   SpecialArrayIterator operator --(int);
   int operator *()
   {
     return parray->valueAt(index);
   }
   bool operator ==(SpecialArray &);
   // etc
private:
   SpecialArray *parray;
   int index;
   // etc
};

ahora i y e pueden usarse legalmente para la iteración y el acceso de valores de SpecialArray


Para responder a su pregunta, las funciones gratuitas begin () y end () no hacen nada más que llamar a las funciones .begin () y .end () del miembro del contenedor. Desde <iterator> , incluido automáticamente cuando utiliza cualquiera de los contenedores estándar como <vector> , <list> , etc., obtiene:

template< class C > 
auto begin( C& c ) -> decltype(c.begin());
template< class C > 
auto begin( const C& c ) -> decltype(c.begin()); 

La segunda parte de tu pregunta es por qué prefieren las funciones gratuitas si todo lo que hacen es llamar a las funciones miembro de todos modos. Eso realmente depende del tipo de objeto v en tu código de ejemplo. Si el tipo de v es un tipo de contenedor estándar, como vector<T> v; entonces no importa si usa las funciones gratuitas o miembros, hacen lo mismo. Si su objeto v es más genérico, como en el siguiente código:

template <class T>
void foo(T& v) {
  auto i = v.begin();     
  auto e = v.end(); 
  for(; i != e; i++) { /* .. do something with i .. */ } 
}

Luego, al usar las funciones de miembro, se rompe el código para las matrices T = C, las cadenas C, las enumeraciones, etc. Al utilizar las funciones que no son miembros, se anuncia una interfaz más genérica que las personas pueden ampliar fácilmente. Al usar la interfaz de función gratuita:

template <class T>
void foo(T& v) {
  auto i = begin(v);     
  auto e = end(v); 
  for(; i != e; i++) { /* .. do something with i .. */ } 
}

El código ahora funciona con arreglos T = C y cadenas C. Ahora escribe una pequeña cantidad de código de adaptador:

enum class color { RED, GREEN, BLUE };
static color colors[]  = { color::RED, color::GREEN, color::BLUE };
color* begin(const color& c) { return begin(colors); }
color* end(const color& c)   { return end(colors); }

También podemos hacer que su código sea compatible con enumeraciones iterables. Creo que el punto principal de Herb es que usar las funciones gratuitas es tan fácil como usar las funciones miembro, y le da a tu código compatibilidad hacia atrás con los tipos de secuencia C y hacia adelante la compatibilidad con tipos de secuencia no stl (¡y tipos stl futuros!), con bajo costo para otros desarrolladores.


Usar las funciones gratuitas de begin y end agrega una capa de indirección. Usualmente eso se hace para permitir más flexibilidad.

En este caso, puedo pensar en algunos usos.

El uso más obvio es para C-arrays (no punteros c).

Otro es cuando se trata de usar un algoritmo estándar en un contenedor no conforme (es decir, al contenedor le falta un método .begin() ). Asumiendo que no puede simplemente arreglar el contenedor, la siguiente mejor opción es sobrecargar la función de begin . Herb sugiere que uses siempre la función de begin para promover la uniformidad y consistencia en tu código. En lugar de tener que recordar qué método de soporte de contenedores begin y qué función de necesidad begin .

Como un aparte, la próxima revisión de C ++ debe copiar la notación pseudo-miembro de D's. Si a.foo(b,c,d) no está definido, en su lugar prueba foo(a,b,c,d) . Es solo un poco de azúcar sintáctico para ayudarnos a los humanos pobres que prefieren el orden de los sujetos y luego los verbos.





c++11