2012-04-27 16 views
15

Tuve algunos problemas al intentar implementar una función de plantilla de macro-tipo de prueba de igualdad inteligente en Visual C++ 2010 que tenía que ver con un bug in VS in regard to default arguments of template functions. Lo arreglé envolviendo el valor del parámetro en una función adicional, ¡pero ahora descubrí que no puedo usar la función dos veces en una línea! archivo¿Por qué el uso de esta función C++ dos veces en una línea causa un error de compilación?

Cabecera: archivo

// example.h 
#pragma once 

#include <limits> 

namespace myspace 
{ 

// Need to define this separately to avoid a Visual Studio bug 
template<typename T> T epsilon() { return std::numeric_limits<T>::epsilon(); } 

// A generic equality test 
template<typename T> inline bool smartEqual(
    const T &v1, 
    const T &v2, 
    const T &eps = epsilon<T>()) 
{ 
    return (v1 == v2); 
} 

// Template specialization for floating-point numbers 
template<> bool smartEqual<float>(
    const float &v1, 
    const float &v2, 
    const float &eps); 

} // namespace myspace 

Fuente:

// example.cpp 
#include "example.h" 

using namespace std; 
using namespace myspace; 

// equal-macro specialization for floats using epsilon 
template<> bool myspace::smartEqual<float>(
    const float &v1, 
    const float &v2, 
    const float &eps) 
{ 
    return (fabs(v1 - v2) < eps); 
} 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    float a,b; 
    bool x = smartEqual(a,b); // works ok 
    bool x = smartEqual(a,b) && smartEqual(b,a); // error 
    return 0; 
} 

de informar del error de la siguiente manera:

------ Build empezar: Proyecto: prueba, Configuración: Depurar Win32 ------
test.cpp
c: \ users \ ninja \ documents \ visual studio 2010 \ projects \ test \ test \ test.cpp (24): error C2440: 'argumento predeterminado': no ​​se puede convertir de 'const float *' a 'const float &'
Motivo: no se puede convertir de 'const float *' a 'float const'
no hay contexto en el que esta conversión es posible

la línea en cuestión es en la que trato de llamar smartEqual() usando el doble de la lógica AND .

No entiendo por qué sucede esto. Cambiar "eps" de un tipo de referencia a un tipo de valor directo lo soluciona, pero me gustaría saber qué estaba pasando.

Gracias!

+0

Si usted instancia 'smartEqual <>()' 2 veces con 2 tipos _distinto_, no lo hará te deja con 2 versiones de 'epsilon()' que difieren solo en el tipo de devolución? –

+0

@Pavel: Esa también es una pregunta interesante, pero como int ei = epsilon (); float ef = epsilon (); compila OK, parece que de alguna manera son funciones separadas. Quiero decir, el compilador de alguna manera distingue entre ellos. No estoy seguro de cómo. – neuviemeporte

Respuesta

13

Creo que ahora has golpeado this VS10 bug.

Su código compila OK en VS11 Beta.

Usted podría evitar que el valor por defecto (que parece ser un problema importante para VS10) cambiando smartEqual a:

template<typename T> inline bool smartEqual(
    const T &v1, 
    const T &v2) 
{ 
    return (v1 == v2); 
} 

y simplemente especializada para la flotación (y doble) así:

template<> bool myspace::smartEqual<float>(
    const float &v1, 
    const float &v2) 
{ 
    return (fabs(v1 - v2) < std::numeric_limits<float>::epsilon()); 
} 


Otra opción es para cambiar el parámetro epsilon para pasar por valor:

template<typename T> inline bool smartEqual(
    const T &v1, 
    const T &v2, 
    T eps = epsilon<T>()) 
{ 
    return (v1 == v2); 
} 
+1

¡Sujetame, estoy ardiendo! Me estremezco al pensar qué otros errores podría desentrañar antes de que termine el día. ;) – neuviemeporte

+0

@neuviemeporte MS debería contratarte para probar VS antes de lanzarlo. –

+0

@neuviemeporte y no se olvide de esta pequeña gema: 'plantilla clase A {class B: public C; }; ' –

2
Código

falló en VS2010 pero está bien en el compilador de Intel. se parece a un error en VS2010

2

Después de cierta consideración, he decidido ir con una otra solución que @Fraser sugiere (aunque me dio la inspiración de él) y escribir mi propia respuesta:

  1. La primera solución me roba la flexibilidad de poder usar un valor personalizado de eps.
  2. La segunda solución con el valor de transferencia se siente mal, especialmente si en el futuro decidiré usar esta función para algunos tipos más inventados.

Dado que VS parece superado con errores en cuanto a los valores predeterminados de los parámetros (¿solo en plantillas?), Parece que lo más sensato es eludir el problema creando dos versiones de smartEqual; con y sin la EPS (utilizando el valor predeterminado), lo que hace más o menos lo mismo, si no es la forma más concisa:

// An equality test that doesn't require the value of eps, default will be used 
template<typename T> inline bool smartEqual(
    const T &v1, 
    const T &v2) 
{ 
    return (v1 == v2); 
} 

// Float specialization: return (fabs(v1 - v2) < std::numeric_limits<float>::epsilon()); 
template<> inline bool smartEqual<float>(
    const float &v1, 
    const float &v2); 

// A custom-eps value equality test 
template<typename T> inline bool smartEqual(
    const T &v1, 
    const T &v2, 
    const T &eps) 
{ 
    return (v1 == v2); 
} 

// Float specialization: return (fabs(v1 - v2) < eps); 
template<> bool smartEqual<float>(
    const float &v1, 
    const float &v2, 
    const float &eps); 
Cuestiones relacionadas