stringstream和nullptr的子类

时间:2019-01-26 06:15:32

标签: c++ templates gcc 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;
}

1 个答案:

答案 0 :(得分:4)

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

您的代码会在libstdc ++中实例化std::__is_insertable<std::basic_ostream<char>&, std::nullptr_t&, void>,然后让我们看一下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通常实例化为std::false_type的派生类。

现在,由于错误51577,您的operator<<在此处可见,从而使parital的专业性成为最佳选择。但是,在实例化__is_insertable时,由于某种原因您的operator<<是不可见的,因此由于operator<<的模棱两可的重载而发生了错误。


请注意GCC 9 compiles此代码。这是因为有一个new overload

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

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


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

  

对于后缀表达式为从属名称的函数调用,使用以下常规查找规则([basic.lookup.unqual],[basic.lookup.argdep])找到候选函数,除了:

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

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

  •   

您当然无法从模板定义上下文中找到您的operator<<。参数的类型为std::basic_ostream<char>std::nullptr_t,因此associated namespaces不包含全局名称空间。因此,找不到您的operator<<