Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

subclass of stringstream and nullptr

This simple code can be compiled by clang++, but not with g++. Is there anything undefined in it? (the template function is needed to make clang happy) GCC 8.2.0 (used with -std=c++17) says operator<< is ambiguous, it shows a list of candidates, but my template function is not even among them.

#include <cstddef>
#include <utility>
#include <sstream>

template<class Out>
Out&& operator<<(Out&& out, std::nullptr_t) {
  out << "nullptr";
  return std::forward<Out>(out); }

struct A : std::stringstream { };

int main() {
  A{} << nullptr;
}
like image 656
Koosha Avatar asked Jan 26 '19 06:01

Koosha


1 Answers

I believe this is caused by Bug 51577 of GCC.

Your code results in the instantiation of std::__is_insertable<std::basic_ostream<char>&, std::nullptr_t&, void> in libstdc++, then let's look at the definition of this struct:

template<typename _Ostream, typename _Tp, typename = void>
  struct __is_insertable : false_type {};

template<typename _Ostream, typename _Tp>
  struct __is_insertable<_Ostream, _Tp,
                         __void_t<decltype(declval<_Ostream&>()
                                           << declval<const _Tp&>())>>
                                  : true_type {};

If everything goes right, your operator<< is invisible here1, then the partial specialization is disabled by SFINAE, and __is_insertable is instantiated normally to be a derived class of std::false_type.

Now due to Bug 51577, your operator<< is visible here, causing the parital specialization to be a perfect match. However, when instantiating __is_insertable, your operator<< is invisible for some reason, so an error occurs because of ambiguous overload for operator<<.


Note GCC 9 compiles this code. This is because there is a new overload

basic_ostream& operator<<( std::nullptr_t );

... added in C++17, so __is_insertable can be successfully instantiated whether or not your operator<< is visible. The bug is still there.


1 This is because [temp.dep.candidate]/1:

For a function call where the postfix-expression is a dependent name, the candidate functions are found using the usual lookup rules ([basic.lookup.unqual], [basic.lookup.argdep]) except that:

  • For the part of the lookup using unqualified name lookup, only function declarations from the template definition context are found.

  • For the part of the lookup using associated namespaces ([basic.lookup.argdep]), only function declarations found in either the template definition context or the template instantiation context are found.

Your operator<< cannot be found from the template definition context of course. The arguments are of types std::basic_ostream<char> and std::nullptr_t, so the associated namespaces do not contain the global namespace. As a result, your operator<< shouldn't be found.

like image 170
xskxzr Avatar answered Oct 29 '22 04:10

xskxzr