C++范围视图迭代器和operator->

6

如果我执行一个变换视图操作,我发现视图迭代器不支持 operator->。因此在代码术语中

void transform_view( )
{
    struct Z { int a; };

    std::ranges::single_view view { Z { 99 } };

    auto transform_view { std::ranges::views::transform( view, []( auto const& z ){ return Z { z.a + 10 }; } ) };

    // The operator-> does not compile here as there is none defined
    // auto transform_view_first_value { transform_view.begin( )->a };
}

如预期的那样,24.7.6.3标准定义的range.transform.iterator没有operator->符号。

我觉得这有点令人惊讶,尝试了解为什么它没有一个。我在标准和各种网站上没有找到关于视图迭代器通常期望的概述,以及为什么至少其中一些没有operator->。

有人知道答案或有关此问题的良好信息来源吗?


可能由于 -> 运算符需要创建临时值,返回指针可能会很棘手/不可能实现,如果在相同的迭代器位置多次调用 -> 也会效率低下。 - Alan Birtles
1个回答

3
简化来说,operator-> 的原型是:
T* operator->();

为了创建这个运算符,你必须能够返回指向 T 的指针,并且在从运算符返回后,此指针必须有效。因此,迭代器必须为该指针存储一个临时对象并控制其生命周期。
这将创建一些问题:
- 假定迭代器的存储成本很低,但现在它们可能包含 T 的隐藏副本。 - 它违反了设计理念之一:范围不能拥有元素。 - 范围的设计指南之一是 适配器是惰性求值的,因此当调用 operator-> 时才会创建对象。每次调用运算符都应该销毁内部对象并返回新的指针。如果我们决定将对象保留为缓存,则必须向迭代器添加更多逻辑以控制对象的生命周期和失效。
它还引发了一些问题,这些问题必须由标准回答:谁拥有那个对象?修改它是否安全?当迭代器被复制时会发生什么?当迭代器被移动时会发生什么?指针何时失效?
我认为没有添加operator->是因为它使实现更加困难,但它并没有增加任何改进:您可以使用operator*来实现相同的行为。然而,此操作符返回T的副本,因此对象的所有者和其生命周期是明确的。

所以,基本上你的意思是range是视图并且应该是轻量级的。 - apple apple
此外,operator-> 不需要返回 T* - apple apple
我认为我可以总结出,operator->带来的问题比它解决的问题还多。是的,你是正确的,operator->不需要返回T*。我所能想到的唯一解决这些问题的选项是返回类似于std::unique_ptr<T>之类的东西,但我认为这只会增加更多的复杂性。 - J. Calleja

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接