c++ - español - ¿Se optimizará la ineficacia de los operadores postfix++/- para los iteradores STL?
stl c++ español (1)
Sé que las versiones de posfijo de los operadores de incremento / disminución generalmente serán optimizadas por el compilador para los tipos incorporados (es decir, no se realizará ninguna copia), pero ¿es este el caso para los iterator
?
En esencia, son operadores sobrecargados, y podrían implementarse de muchas maneras, pero dado que su comportamiento está estrictamente definido, ¿ pueden optimizarse? En caso afirmativo, ¿son compiladores cualquiera / muchos?
#include <vector>
void foo(std::vector<int>& v){
for (std::vector<int>::iterator i = v.begin();
i!=v.end();
i++){ //will this get optimised by the compiler?
*i += 20;
}
}
En el caso específico de std::vector
en la implementación STL de GNU GCC (versión 4.6.1), no creo que haya una diferencia de rendimiento en niveles de optimización suficientemente altos.
La implementación para los iteradores directos en vector
es proporcionada por __gnu_cxx::__normal_iterator<typename _Iterator, typename _Container>
. Veamos su operador constructor y postfix ++
:
explicit
__normal_iterator(const _Iterator& __i) : _M_current(__i) { }
__normal_iterator
operator++(int)
{ return __normal_iterator(_M_current++); }
Y su ejemplificación en el vector
:
typedef __gnu_cxx::__normal_iterator<pointer, vector> iterator;
Como puede ver, internamente realiza un incremento de postfijo en un puntero ordinario, luego pasa el valor original a través de su propio constructor, que lo guarda en un miembro local. Este código debe ser trivial para eliminar a través del análisis de valor muerto.
Pero, ¿está optimizado realmente? Vamos a averiguar. Código de prueba:
#include <vector>
void test_prefix(std::vector<int>::iterator &it)
{
++it;
}
void test_postfix(std::vector<int>::iterator &it)
{
it++;
}
-Os
salida (en -Os
):
.file "test.cpp"
.text
.globl _Z11test_prefixRN9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEE
.type _Z11test_prefixRN9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEE, @function
_Z11test_prefixRN9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEE:
.LFB442:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
movl 8(%ebp), %eax
addl $4, (%eax)
popl %ebp
.cfi_def_cfa 4, 4
.cfi_restore 5
ret
.cfi_endproc
.LFE442:
.size _Z11test_prefixRN9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEE, .-_Z11test_prefixRN9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEE
.globl _Z12test_postfixRN9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEE
.type _Z12test_postfixRN9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEE, @function
_Z12test_postfixRN9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEE:
.LFB443:
.cfi_startproc
pushl %ebp
.cfi_def_cfa_offset 8
.cfi_offset 5, -8
movl %esp, %ebp
.cfi_def_cfa_register 5
movl 8(%ebp), %eax
addl $4, (%eax)
popl %ebp
.cfi_def_cfa 4, 4
.cfi_restore 5
ret
.cfi_endproc
.LFE443:
.size _Z12test_postfixRN9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEE, .-_Z12test_postfixRN9__gnu_cxx17__normal_iteratorIPiSt6vectorIiSaIiEEEE
.ident "GCC: (Debian 4.6.0-10) 4.6.1 20110526 (prerelease)"
.section .note.GNU-stack,"",@progbits
Como puede ver, exactamente el mismo ensamblaje sale en ambos casos.
Por supuesto, esto puede no ser necesariamente el caso de iteradores personalizados o tipos de datos más complejos. Pero parece que, para el vector
específicamente, el prefijo y el postfijo (sin capturar el valor de retorno del sufijo) tienen un rendimiento idéntico.