Почему "bool c = nullptr;" компилирует (С++ 11)?
Я не понимаю, почему следующий код компилирует?
int main()
{
//int a = nullptr; // Doesn't Compile
//char b = nullptr; // Doesn't Compile
bool c = nullptr; // Compiles
return 0;
}
тогда как в разделе комментариев нет.
Я уже прошел и .
Оба bool
и nullptr
являются ключевыми словами, поэтому что уникально для других типов данных?
Ответы
Ответ 1
По той же причине, что и
if( p ) { ... }
компилирует: любое значение базового типа неявно преобразуется в boolean, при преобразовании 0
в false
и любом другом значении в true
.
Первоначально значения базового типа должны были преобразовываться в bool
для совместимости с C. C первоначально не имел тип bool
, но любое числовое выражение могло бы использоваться как логическое (с соглашением 0 == false
). И теперь мы попадаем в спутник обратной совместимости. nullptr
должен поддерживать идиоматические конструкции, такие как if(p)
, особенно для случаев, когда старый литерал кода 0
или NULL
заменяется на nullptr
. Например. такой код, как if(p)
, может быть вызван расширением макроса или кодом шаблона.
Добавление: техническое объяснение того, почему nullptr
не преобразуется, например. int
.
Так как nullptr
неявно преобразуется в bool
, а bool
(к сожалению) неявно преобразуется в int
, можно ожидать, что nullptr
также должен преобразовать в int
. Но точка nullptr
заключается в том, что она должна вести себя как значение указателя. И хотя указатели неявно преобразуются в bool
, они не преобразуются неявно в числовые типы.
Упорядочение такого ограничения для пользовательского типа, однако, не является полностью простым. Преобразование operator bool
будет вызываться для преобразования в int
, если оно присутствует. Одним из решений С++ 11 для ограничения этого ограничения является преобразование оператора преобразования, ограниченного a std::enable_if
следующим образом:
#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.
}
Ответ 2
С++ 11 §4.12 Логические преобразования
Значение арифметики, неперечисленного перечисления, указателя или указателя на тип члена может быть преобразовано в prvalue типа bool
. Нулевое значение, значение нулевого указателя или значение указателя нулевого элемента преобразуется в false
; любое другое значение преобразуется в true
. Значение типа std::nullptr_t
может быть преобразовано в prvalue типа bool
; результирующее значение false
.
Верно, что nullptr
является ключевым словом, но это литерал нулевого указателя, а не ту же роль, что и bool
. Подумайте о булевых литералах, true
и false
также являются ключевыми словами.
Ответ 3
В http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#654 Джейсон Мерриль утверждает
Все, что мы можем сделать с помощью произвольного указателя, мы также должны иметь отношение к nullptr_t.
Я думаю, что следующий (слегка искусственный) пример поддерживает этот аргумент (хотя я не совсем уверен, был ли он предназначен для этого случая)
template<typename T, typename P>
void safeProcess(T pointer, P &processor) {
bool isNonNull(pointer);
if(isNonNull) {
processor.process(pointer);
}
}
Что позволит передавать nullptr
вместе с другими типами указателей, совместимыми с тем, что принимает processor.process
.
Ответ 4
С++ 11 исправляет это, вводя новое ключевое слово, чтобы служить в качестве выделенной константы нулевого указателя: nullptr. Он имеет тип nullptr_t, который неявно конвертируется и сопоставим с любым типом указателя или типа указателя на член. Он не является неявно конвертируемым или сопоставимым с целыми типами, за исключением bool. Хотя в первоначальном предложении указано, что значение rvalue типа nullptr не должно быть конвертируемым в bool, рабочая группа основного языка решила, что такое преобразование было бы желательно для согласованности с обычными типами указателей. Предлагаемые изменения в формулировке были единогласно проголосованы в рабочем документе в июне 2008 года. [2]
Для соображений обратной совместимости 0 остается действительной константой нулевого указателя.
char *pc = nullptr; // OK
int *pi = nullptr; // OK
bool b = nullptr; // OK. b is false.
int i = nullptr; // error