Мне любопытно узнать, как nullptr
работает. Стандарты N4659 и N4849 гласят:
- он должен иметь тип
std::nullptr_t
; - вы не можете взять его адрес;
- это может быть непосредственно преобразовано в указатель и указатель на член;
sizeof(std::nullptr_t) == sizeof(void*)
;- его преобразование в
bool
естьfalse
; - его значение может быть преобразовано в целочисленный тип идентично
(void*)0
, но не обратно;
Так что это в основном константа с тем же значением, что и у (void*)0
, но у нее другой тип. Я нашел реализацию std::nullptr_t
на моем устройстве, и это следующим образом.
#ifdef _LIBCPP_HAS_NO_NULLPTR
_LIBCPP_BEGIN_NAMESPACE_STD
struct _LIBCPP_TEMPLATE_VIS nullptr_t
{
void* __lx;
struct __nat {int __for_bool_;};
_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR nullptr_t() : __lx(0) {}
_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR nullptr_t(int __nat::*) : __lx(0) {}
_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR operator int __nat::*() const {return 0;}
template <class _Tp>
_LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR
operator _Tp* () const {return 0;}
template <class _Tp, class _Up>
_LIBCPP_INLINE_VISIBILITY
operator _Tp _Up::* () const {return 0;}
friend _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR bool operator==(nullptr_t, nullptr_t) {return true;}
friend _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR bool operator!=(nullptr_t, nullptr_t) {return false;}
};
inline _LIBCPP_INLINE_VISIBILITY _LIBCPP_CONSTEXPR nullptr_t __get_nullptr_t() {return nullptr_t(0);}
#define nullptr _VSTD::__get_nullptr_t()
_LIBCPP_END_NAMESPACE_STD
#else // _LIBCPP_HAS_NO_NULLPTR
namespace std
{
typedef decltype(nullptr) nullptr_t;
}
#endif // _LIBCPP_HAS_NO_NULLPTR
Я больше заинтересован в первой части, хотя. Кажется, он удовлетворяет пунктам 1-5, но я понятия не имею, почему у него есть подкласс __nat и все, что с ним связано. Я также хотел бы знать, почему это терпит неудачу на интегральных преобразованиях.
struct nullptr_t2{
void* __lx;
struct __nat {int __for_bool_;};
constexpr nullptr_t2() : __lx(0) {}
constexpr nullptr_t2(int __nat::*) : __lx(0) {}
constexpr operator int __nat::*() const {return 0;}
template <class _Tp>
constexpr
operator _Tp* () const {return 0;}
template <class _Tp, class _Up>
operator _Tp _Up::* () const {return 0;}
friend constexpr bool operator==(nullptr_t2, nullptr_t2) {return true;}
friend constexpr bool operator!=(nullptr_t2, nullptr_t2) {return false;}
};
inline constexpr nullptr_t2 __get_nullptr_t2() {return nullptr_t2(0);}
#define nullptr2 __get_nullptr_t2()
int main(){
long l = reinterpret_cast<long>(nullptr);
long l2 = reinterpret_cast<long>(nullptr2); // error: invalid type conversion
bool b = nullptr; // warning: implicit conversion
// edditor error: a value of type "std::nullptr_t" cannot be used to initialize an entity of type "bool"
bool b2 = nullptr2;
if (nullptr){}; // warning: implicit conversion
if (nullptr2){};
};
c++
c++17
nullptr
null-pointer
Fullfungo
источник
источник
nullptr_t
это фундаментальный тип. Как этоint
реализовано?#ifdef _LIBCPP_HAS_NO_NULLPTR
. Это кажется лучшим решением проблемы, когда компилятор не предоставляетnullptr
.nullptr_t
это фундаментальный тип. Реализация его как типа класса не дает соответствующей реализации. Смотрите комментарий Криса.is_class
иis_null_pointer
оба не могут быть правдой для одного типа. Только одна из функций категории основного типа может возвращать true для определенного типа.Ответы:
Он работает самым простым способом: по указу . Это работает, потому что стандарт C ++ говорит, что работает, и работает так, как работает, потому что стандарт C ++ говорит, что реализации должны заставить его работать таким образом.
Важно признать, что это невозможно реализовать
std::nullptr_t
с использованием правил языка C ++. Преобразование из константы с нулевым указателем типаstd::nullptr_t
в указатель не является определяемым пользователем преобразованием. Это означает, что вы можете перейти от константы нулевого указателя к указателю, а затем с помощью пользовательского преобразования в какой-либо другой тип, все в одной неявной последовательности преобразования.Это невозможно, если вы реализуете
nullptr_t
как класс. Операторы преобразования представляют пользовательские преобразования, а правила неявной последовательности преобразования C ++ не допускают более одного пользовательского преобразования в такой последовательности.Таким образом, код, который вы разместили, является хорошим приближением
std::nullptr_t
, но это не более того. Это не законная реализация типа. Вероятно, это было из более старой версии компилятора (оставленной в целях обратной совместимости) до того, как компилятор обеспечил надлежащую поддержкуstd::nullptr_t
. Вы можете увидеть это тем , что#define
Snullptr
, в то время как C ++ 11 говорит , чтоnullptr
это ключевое слово , а не макрос.C ++ не может реализовать
std::nullptr_t
, так же как C ++ не может реализоватьint
илиvoid*
. Только реализация может реализовать эти вещи. Это то, что делает его «фундаментальным типом»; это часть языка .Не существует неявного преобразования константы с нулевым указателем в целочисленные типы. Существует преобразование из
0
в целочисленный тип, но это потому, что это целочисленный литеральный ноль, который ... целое число.nullptr_t
может быть приведен к целочисленному типу (черезreinterpret_cast
), но он может быть неявно преобразован только в указатели и вbool
.источник
std::nullptr_t
. Точно так же, как вы не можете написать тип, который в точности эквивалентен требуемому поведениюint
. Вы можете подобраться ближе, но все равно будут существенные различия. И я не говорю о детекторах признаков,is_class
которые показывают, что ваш тип определяется пользователем. В обязательном поведении основных типов есть вещи, которые вы просто не можете скопировать, используя правила языка.nullptr_t
», вы говорите слишком широко. А высказывание «только реализация может реализовать его» только сбивает с толку. То, что вы имеете в виду, это то, что «nullptr_t
невозможно реализовать» в библиотеке C ++, потому что она является частью базового языка.std::nullptr_t
требуется. Как и C ++, язык не может реализовать тип, который выполняет все, чтоint
требуется.