C ++ nullptr 구현은 어떻게 작동합니까?


13

어떻게 nullptr작동 하는지 궁금 합니다. 표준 N4659 및 N4849는 다음과 같이 말합니다.

  1. 형식이 있어야합니다 std::nullptr_t.
  2. 당신은 그 주소를 취할 수 없습니다;
  3. 포인터와 멤버에 대한 포인터로 직접 변환 될 수 있습니다.
  4. sizeof(std::nullptr_t) == sizeof(void*);
  5. 로의 변환 boolfalse;
  6. 그것의 값은와 동일하게 정수형으로 변환 될 수 (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){};
};

2
nullptr_t기본 유형입니다. 어떻게 int구현됩니까?
LF

9
참고 #ifdef _LIBCPP_HAS_NO_NULLPTR. 이것은 컴파일러가를 제공하지 않을 때 최선의 해결 방법처럼 보입니다 nullptr.
크리스

5
@Fullfungo 표준은 이것이 nullptr_t기본 유형 이라고 말합니다 . 클래스 타입으로 구현한다고해서 적합한 구현은 아닙니다. 크리스의 의견을 참조하십시오.
LF

1
@LF 표준은 기술적으로 기본 유형이 클래스 유형이 아닌 것을 요구합니까?
eerorika

2
@eerorika : is_classis_null_pointer모두 동일한 종류의 사실이 될 수 없습니다. 특정 유형에 대해서는 기본 유형 범주 함수 중 하나만 true를 반환 할 수 있습니다.
Nicol Bolas

답변:


20

nullptr의 작동 방식을 알고 싶습니다.

그것은 가능한 가장 간단한 방법으로 작동합니다 : fiat . C ++ 표준 이 작동 한다고 말했기 때문에 작동하며 C ++ 표준은 구현 이 그러한 방식으로 작동하도록 해야한다고 말했기 때문에 작동 합니다 .

C ++ 언어의 규칙을 사용하여 구현 하는 것이 불가능 하다는 것을 인식하는 것이 중요합니다 std::nullptr_t. 유형의 널 포인터 상수에서 포인터로의 변환은 std::nullptr_t사용자 정의 변환이 아닙니다. 즉, 널 포인터 상수에서 포인터로 이동 한 다음 사용자 정의 변환을 통해 다른 유형으로 단일 암시 적 변환 순서로 모두 변환 할 수 있습니다.

nullptr_t클래스로 구현하면 불가능합니다 . 변환 연산자는 사용자 정의 변환을 나타내며 C ++의 암시 적 변환 시퀀스 규칙은 이러한 시퀀스에서 둘 이상의 사용자 정의 변환을 허용하지 않습니다.

그래서 당신이 게시 한 코드는 근사치입니다 std::nullptr_t.하지만 그 이상입니다. 합법적 인 유형의 구현이 아닙니다. 이것은 컴파일러가에 대한 적절한 지원을 제공하기 전에 이전 버전의 컴파일러 (이전 버전과의 호환성을 위해 남아 있음) 일 것입니다 std::nullptr_t. 당신은 그 사실이 볼 수 #defines의 nullptrC ++ (11)가 말하는 동안, nullptrA는 키워드 가 아닌 매크로.

C ++는 구현할 수 없습니다 std::nullptr_tC ++ 구현되지 수있는 것처럼, int또는 void*. 구현 만이 그러한 것들을 구현할 수 있습니다. 이것이 "기본 유형"이되는 것입니다. 언어 의 일부입니다 .


값은 (void *) 0과 동일하게 정수 유형으로 변환 될 수 있지만 역방향은 아닙니다.

널 포인터 상수에서 정수 유형으로의 암시 적 변환없습니다 . 0정수형으로 의 변환이 있지만 정수 리터럴 0이기 때문에 정수입니다.

nullptr_t를 통해 정수 유형 으로 캐스트 할 수 reinterpret_cast있지만 암시 적으로 포인터 및로만 변환 할 수 있습니다 bool.


4
@Wyck : " fiat "
Nicol Bolas

"C ++ 언어의 규칙을 사용하여 std :: nullptr_t를 구현할 수 없습니다"란 무엇입니까? C ++ 컴파일러가 C ++ 자체로 완전히 작성 될 수 없다는 것을 의미합니까?
북부

3
@northerner :에 필요한 동작과 정확히 동일한 유형을 작성할 수 없음을 의미합니다 std::nullptr_t. 의 동작과 정확히 동일한 유형을 작성할 수없는 것처럼 int. 가까이 갈 수는 있지만 여전히 큰 차이가 있습니다. 그리고 나는 is_class당신의 유형이 사용자 정의되었음을 노출시키는 특성 탐지기에 대해 이야기하고 있지 않습니다 . 언어 규칙을 사용하여 간단히 복사 할 수없는 기본 유형의 필수 동작에 대한 사항이 있습니다.
Nicol Bolas

1
말로 표현하는 퀴즈입니다. "C ++에서 구현할 수 없습니다 nullptr_t"라고 말하면 너무 광범위하게 말할 수 있습니다. 그리고 "구현 만 구현할 수있다"고 말하면 문제가 혼동됩니다. 당신이 말은 즉 nullptr_t"구현되지 않을 수 의 C ++ 라이브러리 때문에 기본 언어의 그것의 일부입니다.
스펜서

1
@Spencer : 아니요, 정확히 내가 말한 것을 의미했습니다. C ++ 언어 std::nullptr_t는 필요한 모든 것을 수행하는 형식을 구현하는 데 사용할 수 없습니다 . C ++처럼 언어 int는 필요한 모든 것을 수행하는 유형을 구현할 수 없습니다 .
Nicol Bolas
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.