어떻게 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){};
};
답변
nullptr의 작동 방식을 알고 싶습니다.
그것은 가능한 가장 간단한 방법으로 작동합니다 : fiat . C ++ 표준 이 작동 한다고 말했기 때문에 작동하며 C ++ 표준은 구현 이 그러한 방식으로 작동하도록 해야한다고 말했기 때문에 작동 합니다 .
C ++ 언어의 규칙을 사용하여 구현 하는 것이 불가능 하다는 것을 인식하는 것이 중요합니다 std::nullptr_t
. 유형의 널 포인터 상수에서 포인터로의 변환은 std::nullptr_t
사용자 정의 변환이 아닙니다. 즉, 널 포인터 상수에서 포인터로 이동 한 다음 사용자 정의 변환을 통해 다른 유형으로 단일 암시 적 변환 순서로 모두 변환 할 수 있습니다.
nullptr_t
클래스로 구현하면 불가능합니다 . 변환 연산자는 사용자 정의 변환을 나타내며 C ++의 암시 적 변환 시퀀스 규칙은 이러한 시퀀스에서 둘 이상의 사용자 정의 변환을 허용하지 않습니다.
그래서 당신이 게시 한 코드는 근사치입니다 std::nullptr_t
.하지만 그 이상입니다. 합법적 인 유형의 구현이 아닙니다. 이것은 컴파일러가에 대한 적절한 지원을 제공하기 전에 이전 버전의 컴파일러 (이전 버전과의 호환성을 위해 남아 있음) 일 것입니다 std::nullptr_t
. 당신은 그 사실이 볼 수 #define
s의 nullptr
C ++ (11)가 말하는 동안, nullptr
A는 키워드 가 아닌 매크로.
C ++는 구현할 수 없습니다 std::nullptr_t
C ++ 구현되지 수있는 것처럼, int
또는 void*
. 구현 만이 그러한 것들을 구현할 수 있습니다. 이것이 “기본 유형”이되는 것입니다. 언어 의 일부입니다 .
값은 (void *) 0과 동일하게 정수 유형으로 변환 될 수 있지만 역방향은 아닙니다.
널 포인터 상수에서 정수 유형으로의 암시 적 변환 은 없습니다 . 0
정수형으로 의 변환이 있지만 정수 리터럴 0이기 때문에 정수입니다.
nullptr_t
를 통해 정수 유형 으로 캐스트 할 수 reinterpret_cast
있지만 암시 적으로 포인터 및로만 변환 할 수 있습니다 bool
.