2012-04-12 20 views
6

He estado jugando con C++ 11 últimamente, y se acercó con la siguiente función de suma:¿El compilador no advierte sobre la pérdida de precisión?

template <typename T> 
inline T sum(const std::function<T (int)> &f, int initial, int end) 
{ 
    T retval = 0; 
    for(int k = initial; k <= end; k++) { 
     retval += f(k); 
    } 
    return retval; 
} 

La idea es que puedo pasar una función lambda y por lo tanto tienen una función ordenada y legible para matemática sumas. Luego trató lo siguiente:

int main() 
{ 
    std::array<double, 2> arr1 = {{ 0.5, 1.5 }}, 
          arr2 = {{ 1.5, -0.5 }}; 
    auto n = sum<int>([&](int k) { return arr1[k]*arr2[k]; }, // Precision loss! 
         0, 1); 
    return 0; 
} 

he recopilado esta usando g ++ 4.6.3: g++ -Wall -pedantic -std=c++0x -o test main.cpp y no da ninguna advertencia sobre la pérdida de precisión comenté en el comentario del código fuente.

Es casi un hecho aquí que sum<int> es algo malo de hacer, pero puede no ser tan obvio en contextos más complejos. ¿Debería el compilador no darse cuenta de que el valor de retorno de mi función lambda es double y advertirme que estoy perdiendo precisión al convertir a int? ¿Hay alguna razón específica por la que no?

+1

La función no tendrá en cuenta la precisión porque el tiempo de ejecución realiza una conversión implícita antes de pasar los argumentos. Sin embargo, en un mundo ideal, al menos debería ver una advertencia en el estudio visual/su IDE para reflejar que esto daría lugar a una pérdida de precisión. pero, ¿de qué tratan las pruebas unitarias? – War

+0

@Wardy en un mundo ideal que no se compilaría (es decir, sería un error, no una advertencia) sin una solicitud explícita de causar esta pérdida de precisión. –

+0

ok ... bueno soy un desarrollador de C# y tengo que seguir las reglas de .net (no estoy seguro de si también está usando un compilador .net) pero dice en la documentación que donde se espera un tipo menos preciso y un implícito el reparto se aplica ... se considera una característica del lenguaje. Aunque estoy de acuerdo ... un error sería el sueño absoluto en tu escenario pero no es común para la plataforma en mi experiencia. – War

Respuesta

5

Parece totalmente razonable. Le está diciendo al compilador que no se moleste con la Deducción del argumento de la plantilla (es decir, use sum<double>), sino que diga explícitamente que use sum<int>. Eso es tan bueno como un elenco explícito.

[Editar] ¿Qué hay de

template <typename F> 
auto sum(F const& f, int initial, int end) -> decltype(f(initial)) 
{ 
    auto retval = f(initial++); 
    while(initial <= end) { 
     retval += f(initial++); 
    } 
    return retval; 
} 
+0

Es bastante extraño si las advertencias relativas a las declaraciones en la función _implementation_ dependen de un _way_ en el que se han deducido los argumentos de la plantilla – user396672

+0

@ user396672: ¿A qué "implementación de funciones" se está refiriendo? No hay ninguna instrucción en 'suma (const std :: function & f, ...)' que garantiza una advertencia. – MSalters

+0

return (conversión implícita a int) arr1 [k] * arr2 [k]; – user396672

2

VS11 hace una advertencia:

Advertencia C4189 1 advertencia: 'n': variable local se ha inicializado pero no hace referencia
Advertencia C4244 2 advertencia: '+ =': conversión de 'doble' a 'int', posible pérdida de datos

Editar, en realidad, esa advertencia proviene de usar el código:

template <typename T,typename Func> 
inline T sum(Func f, int initial, int end) 

Recibes una advertencia diferente sobre la mala conversión si usas std::function<T (int)>, por lo que VS sigue siendo bueno en este aspecto. (IMO, generalmente debería tomar functors como un tipo de plantilla en lugar de std :: function)

Incluso tropezar con -Todo no da una advertencia al respecto (Editar: aunque no puedo probar la función std :: versión con cajero automático clang). Parece algo que podría mejorarse.

me pongo esta extraña advertencia:

ConsoleApplication1.cpp:15:51: warning: will never be executed [-Wunreachable-code] 
    auto n = sum<int>([&](int k) { return arr1[k]*arr2[k]; }, // Precision loss! 
                ^~~~ 
1

Si No restringir el funtor y si cambia la línea retval += f(k); a retval += T { f(k) }; de la siguiente manera:

// Machinery to allow caller to indifferently use 
// sum(f, i, j) and sum<R>(f, i, j) 
struct deduced {}; 

template< 
    typename Request = deduced 
    , typename Functor 
    , typename Ret = typename std::conditional< 
     std::is_same<Request, deduced>::value 
     , typename std::result_of<Functor&(int)>::type 
     , Request 
    >::type 
> 
inline Ret sum(Functor f, int initial, int end) 
{ 
    Ret retval = 0; 
    for(int k = initial; k <= end; k++) { 
     retval += Ret { f(k) }; 
    } 
    return retval; 
} 

entonces, en lugar de depender de la voluntad del compilador de advertir, usted hace que requiera para emitir un diagnóstico, ya que una conversión de estrechamiento no se permite dentro de la inicialización de la lista (es decir, la inicialización con llaves).

No creo que haya una manera confiable si restringe el functor a un std::function<Sig>. Eso depende únicamente de cómo la implementación escribió std::function, cuando emite advertencias para reducir las conversiones, e incluso si advierte sobre su propio código.

Cuestiones relacionadas