microsoft - visual c++ 2013
¿Por qué los punteros de función(miembro) se comportan de manera extraña en Visual C++? (3)
Tuve un problema realmente extraño que reduje al siguiente caso de prueba:
#include <iostream>
#include <map>
#include <string>
struct Test
{
std::map<std::string, void (Test::*)()> m;
Test()
{
this->m["test1"] = &Test::test1;
this->m["test2"] = &Test::test2;
}
void test1() { }
void test2() { }
void dispatch(std::string s)
{
if (this->m.at(s) == &Test::test1)
{ std::cout << "test1 will be called..." << std::endl; }
else if (this->m.at(s) == &Test::test2)
{ std::cout << "test2 will be called..." << std::endl; }
(this->*this->m.at(s))();
}
};
int main()
{
Test t;
t.dispatch("test1");
t.dispatch("test2");
}
Salidas
test1 se llamará ...
test1 se llamará ...
cuando las optimizaciones están habilitadas, lo cual es realmente extraño. ¿Que esta pasando?
C ++ 11 5.3.1 describe qué &
hace; en este caso, le da un puntero a la función miembro en cuestión, y el pasaje no exige que este puntero sea único.
Sin embargo, 5.10 / 1 dice sobre ==
:
Dos punteros del mismo tipo se comparan igual si y solo si son ambos nulos, ambos apuntan a la misma función o ambos representan la misma dirección.
La pregunta entonces es ... ¿ test1
y test2
"la misma función"?
Aunque el optimizador los ha colapsado en una sola definición, podría decirse que los dos nombres identifican dos funciones y, como tal, esto parecería ser un error de implementación .
(Sin embargo, tenga en cuenta que al equipo de VS no le importa y lo considera "lo suficientemente válido" para garantizar los beneficios de la optimización. Eso, o social.msdn.microsoft.com/Forums/en/vclanguage/thread/… ).
Me limitaría a usar las cadenas como "controladores" para los punteros de función.
Esto es un subproducto de lo que se refiere a Visual C ++ como Plegado COMDAT idéntico (ICF). Combina funciones idénticas en una sola instancia. Puede desactivarlo agregando el siguiente /OPT:NOICF
a la línea de comando del enlazador: /OPT:NOICF
(desde la interfaz de usuario de Visual Studio se encuentra en Propiedades-> Enlazador-> Optimización-> Habilitar COMDAT plegable )
Puede encontrar detalles en el artículo de MSDN aquí: / OPT (Optimizaciones)
El conmutador es un conmutador de etapa enlazadora, lo que significa que no podrá habilitarlo solo para un módulo específico o una región específica de código (como __pragma( optimize() )
que está disponible para la optimización de etapa de compilación.
En general, sin embargo, se considera una práctica deficiente confiar en punteros de función o punteros de cadena literales ( const char*
) para probar la singularidad. Plegado de cadena es ampliamente implementado por casi todos los compiladores de C / C ++. El plegado de funciones solo está disponible en Visual C ++ en este momento, aunque el uso cada vez más extendido de la metaprogramación de template <> ha incrementado las solicitudes para que esta característica se agregue a las cadenas de herramientas gcc y clang.
Editar: Comenzando con binutils 2.19, el enlazador de oro incluido supuestamente también admite ICF, aunque no he podido verificarlo en mi instalación Ubuntu 12.10 local.
Resulta que el enlazador de Visual C ++ puede fusionar funciones con definiciones idénticas en una.
Si eso es legal o no según C ++, no tengo idea; afecta el comportamiento observable, por lo que parece un error para mí. Sin embargo, alguien más con más información puede querer intervenir.