stringstream和nullptr的子类

Koo*_*sha 5 c++ gcc templates clang language-lawyer

这个简单的代码可以用clang ++编译,但不能用g ++编译.它有什么不确定的吗?(模板函数需要使clang高兴)GCC 8.2.0(与-std = c ++ 17一起使用)表示operator <<不明确,它显示了候选列表,但我的模板函数甚至不在其中.

#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;
}
Run Code Online (Sandbox Code Playgroud)

xsk*_*xzr 5

我相信这是由GCC的Bug 51577引起的。

你的代码导致在std::__is_insertable<std::basic_ostream<char>&, std::nullptr_t&, void>libstdc++中的实例化,然后让我们看看这个 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 {};
Run Code Online (Sandbox Code Playgroud)

如果一切顺利,你operator<<在这里是不可见的1,那么部分特化被SFINAE禁用,并且__is_insertable通常被实例化为 的派生类std::false_type

现在由于错误 51577,您operator<<在此处可见,从而使parital 专业化成为完美匹配。但是,在实例化 时__is_insertableoperator<<由于某种原因您是不可见的,因此会由于 的不明确重载而发生错误operator<<


注意 GCC 9编译此代码。这是因为有一个新的重载

basic_ostream& operator<<( std::nullptr_t );
Run Code Online (Sandbox Code Playgroud)

... 添加在 C++17 中,因此__is_insertable无论您operator<<是否可见,都可以成功实例化。错误仍然存​​在。


1这是因为[temp.dep.candidate]/1

对于后缀表达式是依赖名称的函数调用,使用通常的查找规则([basic.lookup.unqual]、[basic.lookup.argdep])找到候选函数,除了:

  • 对于使用非限定名称查找的查找部分,只能找到来自模板定义上下文的函数声明。

  • 对于使用关联命名空间 ([basic.lookup.argdep]) 的查找部分,只能找到在模板定义上下文或模板实例化上下文中找到的函数声明。

当然,您operator<<无法从模板定义上下文中找到。参数属于std::basic_ostream<char>and类型std::nullptr_t,因此关联的命名空间不包含全局命名空间。结果,你operator<<不应该被发现。