pointer - ¿Por qué compila "bool c=nullptr;"(C++ 11)?
pointers c++ (4)
No entiendo por qué el siguiente código compiles ?
int main()
{
//int a = nullptr; // Doesn''t Compile
//char b = nullptr; // Doesn''t Compile
bool c = nullptr; // Compiles
return 0;
}
mientras que la sección comentada no lo hace.
Ya he pasado por this y this .
Tanto bool
como nullptr
son palabras clave, entonces, ¿qué tiene de especial los otros tipos de datos?
C ++ 11 §4.12 conversiones booleanas
Un prvalor de enumeración aritmética, sin cobertura, puntero o puntero a tipo de miembro se puede convertir a un prvalue de tipo
bool
. Un valor cero, un valor de puntero nulo o un valor de puntero de miembro nulo se convierte enfalse
; cualquier otro valor se convierte entrue
. Un prvalue de tipostd::nullptr_t
se puede convertir a un prvalue de tipobool
; el valor resultante esfalse
.
Es cierto que nullptr
es una palabra clave, pero es un literal de puntero nulo, no el mismo rol que bool
. Piense en los literales booleanos, true
y false
también son palabras clave.
C ++ 11 corrige esto al introducir una nueva palabra clave para servir como una constante de puntero nulo distinguido: nullptr. Es de tipo nullptr_t, que es implícitamente convertible y comparable a cualquier tipo de puntero o tipo de puntero a miembro. No es implícitamente convertible o comparable a los tipos integrales, a excepción de bool. Si bien la propuesta original especificaba que un valor r de tipo nullptr no debería ser convertible en bool, el grupo de trabajo del lenguaje central decidió que dicha conversión sería deseable, para mantener la coherencia con los tipos de punteros regulares. Los cambios de redacción propuestos se votaron por unanimidad en el documento de trabajo en junio de 2008. [2]
Por razones de compatibilidad con versiones anteriores, 0 sigue siendo una constante de puntero nulo válida.
char *pc = nullptr; // OK
int *pi = nullptr; // OK
bool b = nullptr; // OK. b is false.
int i = nullptr; // error
En open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#654 , argumenta Jason Merril
Cualquier cosa que podamos hacer con un puntero arbitrario, deberíamos poder hacer con nullptr_t también.
Creo que el siguiente ejemplo (ligeramente artificial) respalda ese argumento (aunque no estoy totalmente seguro de si fue pensado para este caso)
template<typename T, typename P>
void safeProcess(T pointer, P &processor) {
bool isNonNull(pointer);
if(isNonNull) {
processor.process(pointer);
}
}
Lo cual permitiría pasar nullptr
junto con otros tipos de punteros compatibles con cualquier processor.process
acepte el proceso.
Por la misma razón que
if( p ) { ... }
compila: cualquier valor de tipo básico se convierte implícitamente a booleano, con 0
convirtiendo a false
y cualquier otro valor a true
.
Originalmente, los valores de tipo básicos tenían que convertirse a bool
para compatibilidad con C. C originalmente no tenía un tipo de bool
, pero cualquier expresión numérica se podía usar como booleano (con la convención 0 == false
). Y ahora estamos atrapados en el enredo de compatibilidad hacia atrás. nullptr
tiene que soportar construcciones idiomáticas como if(p)
, especialmente para los casos en que el antiguo código literal 0
o NULL
se reemplaza por nullptr
. Por ejemplo, un código como if(p)
puede ser el resultado de una macro expansión o de un código de plantilla.
Adición : el cómo técnico de por qué nullptr
no convierte a eg int
.
Como nullptr
convierte implícitamente a bool
, y bool
(desafortunadamente) convierte implícitamente a int
, se podría esperar que nullptr
también convierta a int
. Pero el punto de nullptr
es que debe comportarse como un valor de puntero . Y mientras los punteros se convierten implícitamente a bool
, no se convierten implícitamente a tipos numéricos.
Disposición de tal restricción para un tipo definido por el usuario, sin embargo, no es del todo sencillo. Se invocará una conversión operator bool
para la conversión a int
, si está presente. Una solución de C ++ 11 para evitar la restricción es hacer que el operador de conversión sea una plantilla, restringida por std::enable_if
, de la siguiente manera:
#include <type_traits> // std::enable_if, std::is_same
struct S
{
template< class Type >
operator Type* () const { return 0; }
template<
class Bool_type,
class Enabled = typename std::enable_if<
std::is_same<Bool_type, bool>::value, void
>::type
>
operator Bool_type () const { return false; }
};
auto main() -> int
{
bool const b = S(); // OK.
double const* const p = S(); // OK.
int const i = S(); // !Doesn''t compile.
}