【问题标题】:arrow operator (operator->) return type when dereference (operator*) returns by value箭头运算符 (operator->) 取消引用时返回类型 (operator*) 按值返回
【发布时间】:2019-03-22 05:01:05
【问题描述】:

我正在编写一个 InputIterator,它实现了 operator* 和 operator->。

我的 operator* 返回一对(向量)元素的引用。因此,Operator* 按值返回。

根据cppreference:

运算符 -> 的重载必须要么返回一个原始指针,要么返回一个对象(按引用或按值),而运算符 -> 又对其进行了重载。

那我要返回什么?

我无法返回原始指针;这对必须以某种方式物理返回。 那么可能一些带有 operator-> 定义的包装器?是否存在于标准库中,或者一般是怎么做的?

【问题讨论】:

  • 我想我可以返回一个包装器,它将值作为数据成员保存,并定义一个 operator-> 来返回指向其数据成员的指针。但它是否存在于 std 库中?
  • 使用std::pair左右
  • 但是迭代器类或结构应该已经一个“包装器”,它引用了你的“容器”中的一个元素。迭代器最简单的解决方案是包含当前位置,从中您可以获得被引用的“值”(“对象”)。然后很容易返回一个指向被引用元素的指针。
  • 作为一个粗略的规则,如果一个类'operator*()(取消引用)返回一个值而不是一个引用,那么质疑它是否应该有一个operator->() 是合适的。习惯上,object->a(*object).a 通常是对同一实体的引用(或值),如果 operator*() 按值返回,则实现这一点相当复杂,有时甚至是不可能的。
  • @Someprogrammerdude 这不是我的情况,但是如果您在向量中有更多迭代器,例如并按顺序处理它们,存储值类型的迭代器将占用过多空间,而不是迭代器在取消引用时返回包装器/代理/->。

标签: c++ iterator


【解决方案1】:
template<class T>
struct fake_ptr_with_value {
  T t;
  T* operator->() { return std::addressof(t); }
};

返回fake_ptr_with_value&lt;decltype(**this)&gt;

std 没有公开此类帮助器类型。

请注意,由于各种迭代器类要求的规范存在缺陷,这仅适用于InputIterators。根据标准,前向迭代器(以及所有更强大的迭代器,如随机访问迭代器)要求 operator* 返回对真实且稳定的对象的引用。

缺陷在于标准 (a) 在某些情况下需要引用,而伪引用会这样做,(b) 混合了“迭代”和“取消引用”语义。

Rangesv3 具有更解耦的迭代器类别系统,可以解决部分(如果不是全部)这些缺陷。

由于这个问题是关于输入迭代器的,所以这个解决方案就足够了;如果有人想在其他地方使用这种技术,我只是把这个警告包括在内。您的代码可以编译并且似乎可以工作,但是当您将迭代器传递给任何 std 函数时,您几乎肯定会违反 C++ 标准的要求,这意味着您的程序格式错误,不需要诊断。

【讨论】:

  • 当有人这样做 auto const&amp; foo = it-&gt;foo; 他们有一个悬空的参考?
  • @ayxan 是的,他们在那里做了一个悬空的参考。这是输入迭代器的限制之一;您不能保证 *-&gt; 对象长期存在。
  • 有趣的是auto const&amp; ref = *it 没问题,但auto const&amp; foo = it-&gt;foo 悬空。我明白原因,但它有点欺骗。
  • @Ayxan 等你玩(*it).foo
  • 使用decltype(std::addressof(t)) operator-&gt;()不是更好吗?
【解决方案2】:

由于-&gt; will continue "drilling down" until it hits a pointer return type,您的代码可以为嵌入其中的对返回“代理”:

template<class T1, class T2>
class pair_proxy {
    std::pair<T1,T2> p;
public:
    pair_proxy(const std::pair<T1,T2>& p) : p(p) {}
    std::pair<T1,T2>* operator->() { return &p; }
};

注意:我强烈考虑在您的迭代器中嵌入std::pair,并从您的operator -&gt;() 实现中返回一个指向它的指针。

【讨论】:

  • 嵌入是必要成为ForwardIterator。 (这有点不幸,因为对于许多类型,即使使用按值operator* 也可以有效地编写多遍算法。)
猜你喜欢
  • 1970-01-01
  • 2017-04-13
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-10-25
  • 1970-01-01
相关资源
最近更新 更多