11
\$\begingroup\$

I tried to write a nullptr_t class based on the official proposal to be used in C++03 only. The only differences with the proposal are that we can compare two nullptr_t instances and that it is convertible to bool via an overload to void* to avoid unwanted behaviour such as int a = nullptr; for example. Here is the class:

const class nullptr_t { public: // Return 0 for any class pointer template<typename T> operator T*() const { return 0; } // Return 0 for any member pointer template<typename T, typename U> operator T U::*() const { return 0; } // Used for bool conversion operator void*() const { return 0; } // Comparisons with nullptr bool operator==(const nullptr_t&) const { return true; } bool operator!=(const nullptr_t&) const { return false; } private: // Not allowed to get the address void operator&() const; } nullptr = {}; 

I would like to know if there is any actual flaw in this completed implementation or if there is a difference in the behaviour compared to the C++11 type std::nullptr_t besides the namespace that I can't see.

\$\endgroup\$

    2 Answers 2

    13
    \$\begingroup\$

    At the moment, your implementation allows

    auto& p = nullptr; 

    This is forbidden in C++11 as nullptr is an rvalue. You also do not allow the following:

    auto p = nullptr; auto pp = &p; 

    While C++11 does allow it. You are also missing overloads for comparison operators.

    A simple workaround would be to remove the operator& overload and add a macro:

    #define nullptr (nullptr_t()) 

    Also, I'd generally use struct X { ... } const x; instead of const struct X { ... } x;.

    \$\endgroup\$
      7
      \$\begingroup\$

      Another thing I forgot at the time is that the standard specifies that sizeof(nullptr) shall be equal to sizeof(void*).

      3.9.1 - Fundamental types

      3.9.1.10 A value of type std::nullptr_t is a null pointer constant (4.10). Such values participate in the pointer and the pointer to member conversions (4.10, 4.11). sizeof(std::nullptr_t) shall be equal to sizeof(void*).

      So technically speaking, I should have added padding to my class:

      class nullptr_t { // To ensure the size // Should be correctly aligned void* padding; // ... } const nullptr; 
      \$\endgroup\$
      2
      • 1
        \$\begingroup\$Could you provide full implementation including C++11 improvements to nullptr ... I would be grateful.\$\endgroup\$
        – Leo
        CommentedJul 10, 2016 at 21:44
      • \$\begingroup\$That sizeof requirement is a nuisance. I see two issues with adding that padding: 1) I'm not sure that copying around an uninitialized void * is well-defined. I considered replacing it with unsigned char padding[sizeof(void *)], but that didn't solve the next issue, nor would adding empty copy operators. 2) Including a private non-static member variable breaks POD-ness, at least before C++11. I don't see a good way around this.\$\endgroup\$CommentedJun 19, 2018 at 11:27

      Start asking to get answers

      Find the answer to your question by asking.

      Ask question

      Explore related questions

      See similar questions with these tags.