1 回答
要了解问题出在哪里,我们必须看一下示例性实现istream_iterator
(问题源于标准的要求,此处隐含显示):
template < class T, class CharT, class Traits, class Distance >
class istream_iterator
{
/* ... */
typedef std::basic_istream<CharT, Traits> istream_type;
istream_type* stored_istream_ptr;
T value;
public:
/* ... */
istream_iterator& operator++()
{
*stored_istream_ptr >> value; // (A)
return *this;
}
T const& operator*() const
{
return value;
}
/* ... */
};
在行 (A) 中,运算符应用于依赖类型的对象:
value
是类型T
stored_istream_ptr
是类型istream_type
,见 typedef
根据 [temp.dep.type]/8,这两种类型都是依赖的。
对于表达式A >> B
以及任何其他函数调用,首先查找函数的名称(此处:operator>>
)->名称查找,然后从找到的函数集(重载)中选择并调用最可行的函数 - >重载决议。
对于运算符,会同时查找成员函数和非成员函数(例如您的运算符)。
在这种情况下,所涉及的类型是依赖的,因此名称查找的特殊规则适用 [temp.dep.res]/1:
在解析从属名称时,会考虑来自以下来源的名称:
- 在模板定义时可见的声明。
- 来自实例化上下文 (14.6.4.1) 和定义上下文的与函数参数类型相关联的命名空间的声明。
您的运算符已在全局名称空间中定义,该名称空间与既不关联std::basic_istream
也不关联std::pair
。因此,名称解析找不到您的运算符,并且(A) 行中的表达式的重载解析失败。
这可以解释 clang 3.2 的错误信息:
stream_iterator.h:120:17: 错误: 二进制表达式的无效操作数 ('istream_type' (aka 'basic_istream < char, std::char_traits >') 和 'std::pair')
*_M_stream >> _M_value; ~~~~~~~~~~ ^ ~~~~~~~~
它解释了为什么变通办法有效。
另一方面,g++ 似乎只显示了通过名称解析找到的一个重载及其拒绝它的原因(而 clang++ 显示了通过名称解析找到的所有重载,并说明了每个重载被拒绝的原因)。一个 g++ 节目可能是“最合适的”:
template< class CharT, class Traits, class T >
basic_istream<CharT,Traits>& operator>>(basic_istream<CharT,Traits>&&, T&);
也就是说,AFAIK,只是operator>>
在表达式istream_obj >> value
对右值(如get_istream() >> value
)进行操作的情况下调用另一个的包装器。
这与您的问题无关(并且令人困惑)。
两种解决方法:
- 使用包装器类型进行包装
std::pair
,以便您可以operator>>
在与该包装器类型关联的命名空间中定义 - 在命名空间 std 中注入您的运算符(不推荐??)