【问题标题】:C++ ranges view iterators and operator->C++ 范围视图迭代器和运算符->
【发布时间】:2021-04-12 17:53:12
【问题描述】:

如果我执行转换视图操作,我注意到视图迭代器不支持 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-> 的原因。

有没有人知道答案或很好的信息来源?

【问题讨论】:

  • 可能因为 -> 运算符可能必须创建一个临时值并且返回一个指针会很棘手/不可能,如果您在同一个迭代器位置多次调用 -> 它也会是低效

标签: c++


【解决方案1】:

简化,operator-> 的原型是:

T* operator->();

为了创建这个操作符,你必须能够返回一个指向 T 的指针,并且这个指针在你从操作符返回后必须是有效的。因此,迭代器必须为该指针存储一个临时对象并控制其生命周期。

这会产生一些问题:

  • 假设迭代器的存储成本很低,但现在它们可能包含 T 的隐藏副本。
  • 它违背了设计原理之一:ranges cannot own elements
  • 范围的设计准则之一是adaptors are lazy evaluated,因此在调用operator-> 时将创建对象。对运算符的每次调用都应销毁内部对象并返回一个新指针。如果我们决定将对象保留为缓存,我们必须在迭代器中添加更多逻辑来控制对象的生命周期和失效。

它还提出了一些标准必须回答的问题:谁拥有该对象?修改它是否安全?复制迭代器时会发生什么?移动迭代器时会发生什么?指针什么时候失效?

我认为没有添加operator-> 是因为它使实现更加困难,但它并没有增加任何改进:您可以使用operator* 来实现相同的行为。但是,这个操作符返回的是 T 的一个副本,所以对象的所有者和它的生命周期是明确的。

【讨论】:

  • 所以,基本上你是说范围是视图,应该是轻量级的。
  • 另外,operator-> 不需要返回T*
  • 我想我可以总结一下operator-> 产生的问题比它解决的问题多。是的,你是对的,operator-> 不需要返回 T*。我认为可以解决这些问题的唯一选择是返回 std::unique_ptr 之类的内容,但我认为它只会增加更多复杂性。
猜你喜欢
  • 2011-01-21
  • 2020-06-14
  • 1970-01-01
  • 1970-01-01
  • 2011-10-18
  • 2011-02-07
  • 1970-01-01
  • 1970-01-01
  • 2011-07-28
相关资源
最近更新 更多