vectors libreria iterador español ejemplo c++ algorithm stl c++11 iota

libreria - stl c++ español



¿Cuál sería una buena implementación de iota_n(algoritmo faltante del STL) (2)

Como ejemplo al azar, compilé el siguiente código con g++ -S -O2 -masm=intel (GCC 4.7.1, x86_32):

void fill_it_up(int n, int * p, int val) { asm volatile("DEBUG1"); iota_n(p, n, val); asm volatile("DEBUG2"); iota_m(p, n, val); asm volatile("DEBUG3"); for (int i = 0; i != n; ++i) { *p++ = val++; } asm volatile("DEBUG4"); }

Aquí iota_n es la primera versión y iota_m la segunda. El conjunto está en los tres casos esto:

test edi, edi jle .L4 mov edx, eax neg edx lea ebx, [esi+edx*4] mov edx, eax lea ebp, [edi+eax] .p2align 4,,7 .p2align 3 .L9: lea ecx, [edx+1] cmp ecx, ebp mov DWORD PTR [ebx-4+ecx*4], edx mov edx, ecx jne .L9

Con -O3 , las tres versiones son también muy similares, pero mucho más largas (usando movimientos condicionales y punpcklqdq y similares).

Con C ++ 11, el STL ahora tiene una función std::iota (ver una referencia ). A diferencia de std::fill_n , std::generate_n , no existe std::iota_n , sin embargo. ¿Cuál sería una buena implementación para eso? ¿Un bucle directo (alternativa 1) o delegación a std::generate_n con una expresión lambda simple (alternativa 2)?

Alternativa 1)

template<class OutputIterator, class Size, class T> OutputIterator iota_n(OutputIterator first, Size n, T value) { while (n--) *first++ = value++; return first; }

Alternativa 2)

template<class OutputIterator, class Size, class T> OutputIterator iota_n(OutputIterator first, Size n, T value) { return std::generate_n(first, n, [&](){ return value++; }); }

¿Ambas alternativas generarían un código equivalente con la optimización de compiladores?

ACTUALIZACIÓN : incorporó el excelente punto de @Marc Mutz para devolver también el iterador en su punto de destino. Esta es también la forma en que std::generate_n se actualizó en C ++ 11 en comparación con C ++ 98.


Estás tan enfocado en la generación de código que olvidaste tener la interfaz correcta.

Necesita correctamente OutputIterator , pero ¿qué ocurre si desea llamarlo por segunda vez?

list<double> list(2 * N); iota_n(list.begin(), N, 0); // umm... iota_n(list.begin() + N, N, 0); // doesn''t compile! iota_n(list.rbegin(), N, 0); // works, but create 0..N,N-1..0, not 0..N,0..N auto it = list.begin(); std::advance(it, N); iota_n(it, N, 0); // works, but ... yuck and ... slow (O(N))

dentro de iota_n , todavía sabes dónde estás, pero has descartado esa información, por lo que la persona que llama no puede acceder a ella en un tiempo constante.

Principio general: no deseche la información útil.

template <typename OutputIterator, typename SizeType, typename ValueType> auto iota_n(OutputIterator dest, SizeType N, ValueType value) { while (N) { *dest = value; ++dest; ++value; --N; } // now, what do we know that the caller might not know? // N? No, it''s zero. // value? Maybe, but it''s just his value + his N // dest? Definitely. Caller cannot easily compute his dest + his N (O(N)) // So, return it: return dest; }

Con esta definición, el ejemplo anterior se convierte simplemente en:

list<double> list(2 * N); auto it = iota_n(list.begin(), N, 0); auto end = iota_n(it, N, 0); assert(end == list.end());