2011-06-26 8 views
19
void foo (const std::string &s) {} 

int main() { 
    foo(0); //compiles, but invariably causes runtime error 
    return 0; 
} 

El compilador (g ++ 4.4) aparentemente interpreta 0 como char* NULL, y construye s llamando string::string(const char*, const Allocator &a = Allocator()). Lo cual es, por supuesto, inútil, porque el puntero NULL no es un puntero válido para una cadena de caracteres. Esta mala interpretación no se produce cuando intento llamar al foo(1), esto produce un error en tiempo de compilación.¿Cómo evito que los problemas derivados de std :: string se construyan a partir de `0`?

¿Hay alguna posibilidad de conseguir un error o advertencia como en tiempo de compilación cuando accidentalmente llama a una función como

void bar(const std::string &s, int i=1); 

con bar(0), olvidándose de la string, y en realidad lo que significa tener i=0?

+3

No sin una cierta ayuda del compilador. Algunas implementaciones han agregado un 'basic_string (int)' privado para detectar este caso. Si no, supongo que no tienes suerte. –

+0

Me sorprende que este pase gcc, es conocido por sus feos errores/advertencias, pero esperaba algo aquí. ¿Has subido el nivel de advertencia? –

+0

@Matthieu No pude encontrar ninguna opción que me diera una advertencia, pero no me gustan las opciones de advertencia de gcc. '-W -Wall -Wpointer-arith -Wcast-qual' no funciona, en cualquier caso. – leftaroundabout

Respuesta

10

Esto es un poco feo, pero se puede crear una plantilla que va a producir un error cuando una instancia:

template <typename T> 
void bar(T const&) 
{ 
    T::youHaveCalledBarWithSomethingThatIsntAStringYouIdiot(); 
} 

void bar(std::string const& s, int i = 1) 
{ 
    // Normal implementation 
} 

void bar(char const* s, int i = 1) 
{ 
    bar(std::string(s), i); 
} 

Luego de usarlo:

bar(0); // produces compile time error 
bar("Hello, world!"); // fine 
+5

+1 aunque este enfoque falla si 'T' en realidad tiene un método público estático llamado' youHaveCalledBarWithSomethingThatIsntAStringYouIdiot' y no toma argumentos;) –

1

Una solución algo limpio ...

#include <cassert> 

void foo (const std::string &s) 
{ 
    // Your function 
} 

void foo(const char *s) 
{ 
    assert(s != 0); 
    foo(std::string(s)); 
} 
+0

Excepto que esto es tiempo de ejecución, no de tiempo de compilación. – robert

+0

@robert Lo sé, es solo una posible solución. –

-1

Actually static asertos funcionaría también. considere esto:

void foo (const std::string &s) 
{ 
    // Your function 
} 

void foo(const char *s) 
{ 
    #ifdef CPP_OH_X 
    static_assert(s == 0, "Cannot pass 0 as an argument to foo!"); 
    #else 
    typedef int[(s != 0) ? 1 : -1] check; 
    #endif 
    foo(std::string(s)); 
} 

La idea aquí es utilizar static_assert que es una característica próxima en C++ y ya se implementa en varios compiladores; primordialmente los que soportan C++ 0x. Ahora bien, si no está utilizando C++ 0x, puede usar el método alternitivo, que tipicamente tipifica un entero con un valor negitive en caso de falla. Algo que no está permitido y producirá un error al compile time

+0

Esto sin duda parece más limpio que el hack de plantilla, pero no parece funcionar: 'implctstringparam.cpp: En la función 'void foo (const char *)': implctstringparam.cpp: 9: error: 's' no puede aparecer en una expresión constante' – leftaroundabout

+0

La const std :: string & s muy probablemente tenga que ser estática para que funcione la aserción estática. – graphitemaster

+1

El valor de 's' solo se conoce en tiempo de ejecución, cuando se llama a la función: no se puede usar en un' static_assert', que se evalúa en tiempo de compilación. Un 'static_assert' solo puede evaluar expresiones constantes. –

Cuestiones relacionadas