2
4

1 回答 1

2

要了解问题出在哪里,我们必须看一下示例性实现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 中注入您的运算符(不推荐??)
于 2013-05-14T14:40:04.160 回答