c++ - Evite que la función tome const std:: string & de aceptar 0
implicit-conversion (2)
Vale más que mil palabras:
#include<string>
#include<iostream>
class SayWhat {
public:
SayWhat& operator[](const std::string& s) {
std::cout<<"here/n"; // To make sure we fail on function entry
std::cout<<s<<"/n";
return *this;
}
};
int main() {
SayWhat ohNo;
// ohNo[1]; // Does not compile. Logic prevails.
ohNo[0]; // you didn''t! this compiles.
return 0;
}
El compilador no se queja al pasar el número 0 al operador de soporte que acepta una cadena. En cambio, esto se compila y falla antes de ingresar al método con:
terminate called after throwing an instance of ''std::logic_error''
what(): basic_string::_S_construct null not valid
Para referencia:
> g++ -std=c++17 -O3 -Wall -Werror -pedantic test.cpp -o test && ./test
> g++ --version
gcc version 7.3.1 20180303 (Red Hat 7.3.1-5) (GCC)
Mi conjetura
El compilador está usando implícitamente el constructor
std::string(0)
para ingresar el método, lo que genera el mismo problema (google el error anterior) sin ninguna buena razón.
Pregunta
¿Hay alguna forma de arreglar esto en el lado de la clase, para que el usuario de la API no sienta esto y el error se detecte en el momento de la compilación?
Es decir, agregar una sobrecarga
void operator[](size_t t) {
throw std::runtime_error("don''t");
}
No es una buena solución.
La razón por la cual
std::string(0)
es válida, se debe a que
0
es una constante de puntero nulo.
Entonces coincide con el constructor tomando un puntero.
Luego, entra en conflicto con la condición previa de que no se puede pasar un puntero nulo a
std::string
.
Solo el
0
literal se interpretaría como una constante de puntero nulo, si fuera un valor de tiempo de ejecución en un
int
no tendría este problema (porque entonces la resolución de sobrecarga buscaría una conversión
int
).
El literal
1
tampoco es un problema, porque
1
no es una constante de puntero nulo.
Como se trata de un problema de tiempo de compilación (valores no válidos literales), puede detectarlo en tiempo de compilación. Agregue una sobrecarga de este formulario:
void operator[](std::nullptr_t) = delete;
std::nullptr_t
es el tipo de
nullptr
.
Y coincidirá con
cualquier
constante de puntero nulo, ya sea
0
,
0ULL
o
nullptr
.
Y dado que la función se elimina, provocará un error de tiempo de compilación durante la resolución de sobrecarga.
Una opción es declarar una sobrecarga
private
del
operator[]()
que acepta un argumento integral y no lo define.
Esta opción funcionará con todos los estándares de C ++ (1998 en adelante), a diferencia de opciones como
void operator[](std::nullptr_t) = delete
que son válidas desde C ++ 11.
Hacer que el
operator[]()
un miembro
private
causará un error diagnosticable en su ejemplo
ohNo[0]
, a menos que esa expresión sea utilizada por una función miembro o un
friend
de la clase.
Si esa expresión se usa desde una función miembro o
friend
de la clase, el código se compilará pero, dado que la función no está definida, generalmente la compilación fallará (por ejemplo, un error de enlace debido a una función indefinida).