3

为什么不const_iterator提供一个const_iterator::base()函数,来获取对应的非常量iterator就好了reverse_iterator呢?

考虑以下伪代码(例如几何算法):

std::container< point > universe;
auto it = std::cbegin(universe);
std::list< decltype(it) > interesting_subset = sieve(it, std::cend(universe));
auto structure = algorithm(interesting_subset);

universe所有输入点在哪里。在 -之后sieve(),将interesting_subsetcontains 迭代器指向universe's 成员的子集。以下algorithm()构造structure来自的结果interesting_subset,其中包含对 . 成员的引用(迭代器)universe

最后,我想改变points,包含结果structure(比如,移动它们)。但同样我想保护他们在algorithm行动中不被修改,因此我使用std::cbegin/std::cendstd::begin/相对std::end。最后,我只const_iterator引用了 source point

iterator std::container< T >::const_iterator::base() const这是我希望出现在 STL 容器中的成员函数的一个非常用例。

4

4 回答 4

3

为什么 const_iterator 没有像 reverse_iterator 那样提供 const_iterator::base() 函数来获取对应的非常量迭代器?

维护常量安全。正如这里已经详细讨论的那样,提供这样的功能将是非常危险的。

最后,我想改变点,包含到结果结构中(比如,移动它们)。但同样我想在算法操作期间保护它们免受修改,因此我使用 std::cbegin/std::cend 与 std::begin/std::end 相反。最后,我只有对源点的 const_iterator 引用。

好吧,您对base-member 提出了错误的要求。当然它会解决你的问题,但正如所说,这太危险了。让我为你重新提出一个问题:

如果我有一个const_iterator对象和对容器的非 const 访问,我如何有效地(在恒定时间内)获得iterator引用的对象?

这是一个花哨的技巧来做到这一点:

template <typename Container, typename ConstIterator>
typename Container::iterator remove_constness(Container& c, ConstIterator it)
{
    return c.erase(it, it);
}

我不认为这个伎俩有任何功劳。我从https://stackoverflow.com/a/10669041/2079303找到它,他们归功于Howard Hinnant 和 Jon Kalb

正如该答案的评论中所讨论的,此技巧适用于所有标准容器,但不一定适用于所有可能的符合标准的第三方容器,因为它们不需要提供erase.

就个人而言,我更希望标准容器有一个非常量成员函数,可以将给定const_iterator的a 转换为 a iterator,但它们没有,所以你需要解决它。

于 2015-10-29T13:12:39.793 回答
2

出于同样的原因,没有从const T*toT*的转换:常量正确性。

比较reverse_iterator无效,因为“反向迭代器”和“正向迭代器”之间的区别与“常量迭代器”和“非常量迭代器”之间的区别完全正交。后者有后果;前者最终没有。

于 2015-10-29T12:11:24.147 回答
1

这是一个有趣的问题。您希望能够在推理时保护这些点,但在推理完它们后返回对它们的可变引用。

作为对这些点进行推理的结果,您实际上返回的是它们的“名称”或“标识符”。

可以想象,我们可以按名称映射它们,并sieve()返回相关名称的向量。如果我们想避免存储正式名称(唯一编号、文本字符串等)的开销,这样的名称可以只是一个地址。

如果我们使用 const 对象的地址作为名称,那么当然要将其转回对可变对象的引用,我们需要一个const_cast. 这可能被视为我们可以合理使用 const 强制转换的少数几次之一。这样做时,我们应该将其封装在实用程序类中以限制任何后果。

编辑:重新考虑解决方案。这个现在不能被不守规矩的客户端代码滥用。

#include <iostream>
#include <vector>

struct point { int x, y; };

inline std::ostream& operator<<(std::ostream& os, const point& p)
{
    os << "(" << p.x << ", " << p.y << " )";
    return os;
}

struct point_collection
{
    using container_type = std::vector<point>;

    point_collection(container_type points) : _points(std::move(points)) {}

    using const_iterator = const point*;
    using iterator = point*;

    const_iterator begin() const { return &*_points.begin(); }
    const_iterator end() const { return &*_points.end(); }

    // note that this function is only available on a non-const point_collection
    point& mutable_reference(const_iterator input)
    {
        // could put an assert in here to ensure that input is within our range
        return *const_cast<iterator>(input);
    }

    container_type _points;
};


std::vector<point_collection::const_iterator> sieve(point_collection::const_iterator first,
                                                    point_collection::const_iterator last)
{
    std::vector<point_collection::const_iterator> result;

    for ( ; first != last ; ++first )
    {
        if (first->x > 6)
            result.push_back(first);
    }
    return result;
}


point_collection make_a_universe()
{
    return {
        std::vector<point> {
            { 10, 10 },
            { 6, 6 }
        }
    };
}

auto main() -> int
{
    using namespace std;
    auto universe = make_a_universe();

    auto interesting = sieve(universe.begin(), universe.end());

    for (auto point_id : interesting) {
        auto& p = universe.mutable_reference(point_id);
        p.x = -p.x;
        cout << p << endl;
    }
    return 0;
}

预期输出:

(-10, 10 )
于 2015-10-29T11:07:26.927 回答
0

Areverse_iterator不会改变底层对象是否为 const 并且 const_iterator 与迭代器无关(除了可转换和引用的要求),因此您正在比较苹果和橘子。

如果const_iterator确实可以通过它提供对非常量迭代器的访问,base则可以执行以下操作

auto const & a = std::vector<int>(20, 1);
auto it = std::cbegin(a);
*it.base() = 4;

标准允许这样做。

您确实 - 原则上 - 想要规避由const_iterator. 但这就是它的意义所在const_iterator。所以这既不是一个好主意,也不太可能发生,我认为(并希望)。

尽管我认为这是一个XY 问题,但我回答了这个问题,而不是提供有关如何正确执行此操作的指导。

-编辑-

如果您希望 aconst_iterator能够返回 a iterator,那就是iterator您想要的。

你的意图似乎是

  1. 传递const_iterator到不允许更改元素的sieve地方。sieve
  2. 传递相同的迭代器以algorithm允许它修改它们。

您需要来自同一个对象的两种不同的东西。没有人阻止实现者sieve使用it.base(),因此根本无法保证sieve不会改变元素。我再说一遍,这就是问题的重点const_iterator

如果有任何使用 s 改变事物的方法,const_iterator它只会破坏它们。

于 2015-10-29T11:10:39.563 回答