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 std::string(0)
constructor 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.
c++
string
std
implicit-conversion
kabanus
fuente
fuente
operator[]()
que acepte unint
argumento y no lo defina.Respuestas:
El motivo
std::string(0)
es válido, se debe a que0
es un puntero nulo constante. Entonces 0 coincide con el constructor de cadenas que toma un puntero. Luego, el código entra en conflicto con la condición previa a la que no se puede pasar un puntero nulostd::string
.Solo el literal
0
se interpretaría como una constante de puntero nulo, si fuera un valor de tiempo de ejecución en unint
no tendría este problema (porque entonces la resolución de sobrecarga buscaría unaint
conversión). Tampoco es literal1
un problema, porque1
no es un puntero nulo constante.Dado que es 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:
std::nullptr_t
es el tipo denullptr
. Y que coincidirá con cualquier constante puntero nulo, ya sea0
,0ULL
onullptr
. Y dado que la función se elimina, provocará un error de tiempo de compilación durante la resolución de sobrecarga.fuente
std::string
el estándar C ++ no permite pasar un puntero nulo al constructor. Es un comportamiento indefinido, por lo que MSVC puede hacer lo que quiera (como lanzar una excepción).Una opción es declarar una
private
sobrecargaoperator[]()
que acepte un argumento integral y no lo defina.Esta opción funcionará con todos los estándares de C ++ (1998 en adelante), a diferencia de opciones como las
void operator[](std::nullptr_t) = delete
que son válidas desde C ++ 11.Hacer que
operator[]()
unprivate
miembro provoque un error diagnosticable en su ejemploohNo[0]
, a menos que esa expresión sea utilizada por una función miembro ofriend
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).fuente