这个简单的代码可以用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;
}
答案 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<<
。