【问题标题】:casting a returned iterator to const将返回的迭代器强制转换为 const
【发布时间】:2019-02-04 03:11:01
【问题描述】:

我的代码中有以下for 语句:

for (auto Iter = Target.begin(), 
        IterEnd = std::stable_partition(Target.begin(), Target.end(), Check);
    Iter != IterEnd; ++Iter)
    { /* loop statement */ }

关键是循环不会修改容器的元素,因此将迭代器声明为const_iterator 是有意义的。我可以使用cbegin() 轻松解决第一个问题,但第二个更复杂。我不能在stable_partition 中声明cbegin() 和cend(),因为当然stable_partition 需要non const_iterators 来完成它的工作。

一种可能的解决方案是将 auto 替换为正确的类型,在本例中为 std::vector< std::string >::const_iterator。这会强制在第二个分配中从 iterator 转换为 const_iterator。

虽然,我不喜欢它。类型可以轻松快速地变得难以管理,因此我正在寻找一种让我使用 auto 的解决方案,而无需在循环之外声明一些奇怪的东西。有什么建议吗?

【问题讨论】:

  • 我不确定您对类型不可管理性的看法。如果这可以解决您的问题,那么在您的类型声明中明确是一种很好的方法。 auto 很方便,但我不会说它一定比清楚您使用的类型更易于管理。
  • 我肯定会把所有这些东西移出循环序幕。 auto Iter{Target.cbegin()}; decltype(Target)::const_iterator const IterEnd{std::stable_partition(Target.begin(), Target.end(), Check)};
  • 你可以使用decltype(Target.cbegin()) IterEnd
  • 小心你的术语:让迭代器 const doesn't 有意义,因为你会增加它。 ;)
  • 还要注意stable_partition 重新排序Target 的元素(也可能是第一个),所以Iter 可能不指向第一个元素(取决于使用的容器)。在初始化 Iter 之前先调用 stable_partition。

标签: c++ type-conversion const-iterator


【解决方案1】:

我认为最明确的解决方案是将std::stable_partition 拉到for 之前。这将产生一个等效的算法。

问题是stable_partition 返回一个可以修改元素的迭代器。幸运的是,存在从 container::iterator 到 container::const_iterator 的隐式转换(对于大多数标准容器)。要进行转换,您可以使用std::vector<T::const_iterator 或decltyp(Target.cbegin() 或我的个人偏好指定IterEnd 的类型:

auto Iter = Target.cbegin();
decltype(Iter) IterEnd = std::stable_partition(Target.begin(), Target.end(), Check);

for (; Iter != IterEnd; ++Iter)
{
}

为了完整起见,如果您愿意,您可以将所有内容保留在 for 中,但我认为它的可读性较差:

for (auto Iter = Target.cbegin(),
        IterEnd = (decltype(Iter)) std::stable_partition(Target.begin(), Target.end(), Check);
     Iter != IterEnd;
     ++Iter)
{}

【讨论】:

  • 对std::stable_partition 的调用在序言中,因此虽然从哲学上讲我们可能会声称它是“在循环期间”,但实际上并非如此,因此您的解决方案不仅好,而且等同于请求. +1,但考虑消除开头段落的错误
  • 你仍然说 OP“需要”在 for 之前拉 std::stable_partition,这是不正确的,尽管这样看起来确实好多了(我不确定 OP由于序言中的单一声明声明与for 混合在一起,因此可以在没有它的情况下得到他们想要的——也许这就是你的意思?)
  • @LightnessRacesinOrbit hm...他可以通过使用演员表
  • 确实,但我相信问题的重点是“转换为什么?(没有严格地写出所有内容)” - 您的答案确实通过将声明分成两部分来完美解决这个问题,允许我们使用 auto /decltype 在两个阶段。
  • 我想知道 auto x = f(), y = (decltype(x))g() 是否有效(打赌不)
【解决方案2】:

这是通过函数式接口表达想法的一种方式:

#include <vector>
#include <algorithm>
#include <iostream>

namespace detail {
    template<class Container, class F>
    struct const_partitioned_target
    {
        using container_type = std::decay_t<Container>;
        using const_iterator = typename container_type::const_iterator;

        const_partitioned_target(Container& cont, F f)
            : first(cont.cbegin())
            , last(std::partition(cont.begin(), cont.end(), f))
        {

        }

        const_iterator begin() const { return first; }

        const_iterator end() const { return last; }

        const_iterator first, last;
    };
}

template<class Container, class F>
auto const_partitioned_target(Container& cont, F&& f)
{
    return detail::const_partitioned_target<Container, std::decay_t<F>>(cont, std::forward<F>(f));
};

int main()
{
    std::vector<int> Target { 1, 2, 6, 9, 10, 20, 30, 40 };

    auto Check = [](auto&& x)
    {
        return x < 10;
    };

    for(auto&& elem : const_partitioned_target(Target, Check))
    {
        // elem will have the type: int const&
        std::cout << elem << '\n';
    }
}

预期输出:

1
2
6
9

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-06-24
    • 2013-11-25
    • 1970-01-01
    • 2012-06-07
    • 1970-01-01
    • 1970-01-01
    • 2013-10-20
    相关资源
    最近更新 更多