c++c++11stlnullptrstream-operators

operator << (stream output) for nullptr


Consider a piece of generic C++ code which outputs to a stream the values of its arguments in case they are not equal:

#define LOG_IF_NE(a, b) if(a != b) { \
    std::cerr << "Failed because (" << ##a << "=" << (a) << \
        ") != (" << ##b << "=" << (b) << ")"; \
}

This is just an example, the real code throws an exception after writing message to a string stream. This works fine for 2 integers, 2 pointers, etc. for what stream operator << is defined.

int g_b;
int f(int a)
{
    LOG_IF_NE(a, g_b);
    // implementation follows
}

A problem happens when one of the arguments to LOG_IF_NE is nullptr: MSVC++2013 compiler gives error C2593: 'operator <<' is ambiguous.

int *pA;
int g()
{
    LOG_IF_NE(pA, nullptr);
}

The problem happens because nullptr has a special type and operator << is not defined in the STL for that type. An answer at https://stackoverflow.com/a/21772973/1915854 suggests to define operator << for std::nullptr_t

//cerr is of type std::ostream, and nullptr is of type std::nullptr_t
std::ostream& operator << (std::ostream& os, std::nullptr_t)
{
    return os << "nullptr"; //whatever you want nullptr to show up as in the console
}

Is it the right way to solve the problem? Isn't it a bug in C++11/STL that operator<< is not defined for nullptr_t? Is a fix expected in C++14/17? Or was it done on purpose (therefore one's private definition of operator<< could have a pitfall)?


Solution

  • This is LWG #2221, which proposes:

    The obvious library solution is to add a nullptr_toverload, which would be defined something like

    template<class C, class T>
    basic_ostream<C, T>& operator<<(basic_ostream<C, T>& os, nullptr_t) 
    { 
      return os << (void*) nullptr; 
    }
    

    We might also consider addressing this at a core level: add a special-case language rule that addresses all cases where you write f(nullptr) and f is overloaded on multiple pointer types. (Perhaps a tiebreaker saying that void* is preferred in such cases.)

    It isn't in C++14, I don't know whether or not it will make it into C++17. It is a very easy problem to fix yourself, so it's not particularly high priority as far as standards changes go. As you said yourself in the question - it's just a 3 line function.