Search code examples
c++namespacesoperator-overloadingpretty-printargument-dependent-lookup

Cxx-prettyprint (for standard containers) defines its output operators inside namespace std - is this a standard violation?


I have been successfully using cxx-prettyprint: A C++ Container Pretty-Printer to log container values. (See also Pretty-print C++ STL containers) It's working like a charm even on the old VS-2005 (VC8) compiler (with the prettyprint98.hpp header), also works well on VS2017-2019 when using it e.g. to make container values printable in Unit Tests.

While studying its interoperability with Boost.Format, I found to my surprise that it simply works out of the box, when other questions suggest it shouldn't because ADL should fail for a user provided output operator.

Looking into the cxx-pp header I found that it simply works because the library does define its output operator(s) inside the std namespace:

// Main magic entry point: An overload snuck into namespace std.
// Can we do better?

namespace std
{
    // Prints a container to the stream using default delimiters

    template<typename T, typename TChar, typename TCharTraits>
    inline typename enable_if< ::pretty_print::is_container<T>::value,
                              basic_ostream<TChar, TCharTraits> &>::type
    operator<<(basic_ostream<TChar, TCharTraits> & stream, const T & container)
    {
        return stream << ::pretty_print::print_container_helper<T, TChar, TCharTraits>(container);
    }
}    
....

Obviously the authors were not 100% confident with this: qq "Can we do better?"

Adding something to the std namespace is formally UB:

[C++11: 17.6.4.2.1/1]: The behavior of a C++ program is undefined if it adds declarations or definitions to namespace std or to a namespace within namespace std unless otherwise specified. A program may add a template specialization for any standard library template to namespace std only if the declaration depends on a user-defined type and the specialization meets the standard library requirements for the original template and is not explicitly prohibited.

So, is this in cxx-pp formally UB, or is it a template specialization (It doesn't seem like one to me).

Comments as to the practical impact of this, iff UB, would be very welcome.


Solution

  • This is covered by [namespace.std]/1 in the standard (which is quoted in the question but reproducing it here as it is also the answer):

    The behavior of a C++ program is undefined if it adds declarations or definitions to namespace std or to a namespace within namespace std unless otherwise specified.

    There isn't any "otherwise specified" for overloads of operator<< for new user-defined types.

    So the behaviour is just undefined (with no diagnostic required).

    In practical terms it seems like that either the code will work or it won't, but as with all UB we should be careful not to rule anything else out.