我相信这是由 GCC 的Bug 51577 引起的。
你的代码导致std::__is_insertable<std::basic_ostream<char>&, std::nullptr_t&, void>在libstdc++中的实例化,那我们看看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 {};
如果一切顺利,你的operator<<在这里是不可见的1,那么部分特化被SFINAE禁用,__is_insertable被正常实例化为@987654332的派生类@。
现在由于错误 51577,您的 operator<< 在此处可见,导致部分专业化完美匹配。但是,在实例化__is_insertable 时,您的operator<< 由于某种原因是不可见的,因此由于operator<< 的模糊过载而发生错误。
注意 GCC 9 compiles 这段代码。这是因为有一个new overload
basic_ostream& operator<<( std::nullptr_t );
... 在 C++17 中添加,因此无论您的 operator<< 是否可见,都可以成功实例化 __is_insertable。错误仍然存在。
1 这是因为[temp.dep.candidate]/1:
对于后缀表达式是从属名称的函数调用,使用通常的查找规则([basic.lookup.unqual]、[basic.lookup.argdep])找到候选函数,除了:
当然,您的operator<< 无法从模板定义上下文中找到。参数类型为std::basic_ostream<char> 和std::nullptr_t,因此associated namespaces 不包含全局命名空间。因此,您的operator<< 应该找不到。