为什么 const_iterator 不提供像 reverse_iterator 这样的基数?
Why const_iterator does not provide a base like reverse_iterator?
为什么const_iterator
不提供const_iterator::base()
函数,像reverse_iterator
那样获取相应的非常量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()
-ing 之后,interesting_subset
包含 universe
成员子集的迭代器。 algorithm()
从 interesting_subset
构造结果 structure
,它包含对 universe
.
成员的引用(迭代器)
最后,我想将包含的 point
s 更改为结果 structure
(例如,移动它们)。但同样地,我想在 algorithm
操作期间保护它们免受修改,因此我使用 std::cbegin
/std::cend
与 std::begin
/std::end
相反。最后我只有 const_iterator
对来源 point
s.
的引用
这是 iterator std::container< T >::const_iterator::base() const
我希望出现在 STL 容器中的成员函数的一个非常用例。
这是个有趣的问题。您希望能够在对它们进行推理时保护这些点,但是 return 对它们进行推理后对它们的可变引用。
作为对这些点进行推理的结果,您实际上 returning 是他们的 'names' 或 'identifiers'。
我们可以想像地按名称映射它们,并得到 sieve()
到 return 相关名称的向量。如果我们想避免存储正式名称(唯一编号、文本字符串等)的开销,这样的名称可以只是一个地址。
如果我们使用 const 对象的地址作为名称,那么当然要将其转回对可变对象的引用,我们需要一个 const_cast
。这可能被视为我们可以合理地使用 const 转换的少数几次之一。这样做时,我们应该将其封装在实用程序中 class 以限制任何影响。
编辑:重新考虑解决方案。这个现在不能被不守规矩的客户端代码滥用。
#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 )
A reverse_iterator
不会改变底层对象是否为 const 并且 const_iterator 与迭代器无关(除了可转换和引用的要求)所以你是比较苹果和橘子。
如果 const_iterator
确实可以通过 base
提供对非 const 迭代器的访问,则可以执行类似
的操作
auto const & a = std::vector<int>(20, 1);
auto it = std::cbegin(a);
*it.base() = 4;
标准允许这样做。
您确实 - 原则上 - 想要规避 const_iterator
提供的修改保护。但这就是 const_iterator
的意义所在。所以这既不是一个好主意,也不太可能发生,我认为(并希望)。
尽管我认为这是 XY Problem,但我只是回答了问题,而不是提供有关如何正确操作的指导。
-编辑-
如果你想让 const_iterator
能够 return 和 iterator
,你想要的是 iterator
。
你的意图似乎是
- 将
const_iterator
传递给 sieve
,其中 sieve
不允许更改元素。
- 将完全相同的迭代器传递给
algorithm
允许它修改它们。
您需要从同一个对象中获得两种不同的东西。
没有人阻止 sieve
的实现者使用 it.base()
,因此根本无法保证 sieve
不会更改元素。我再说一遍,这就是问题的重点 const_iterator
。
如果有任何方法可以使用 const_iterator
s 改变事物,它只会破坏它们。
出于同样的原因,没有从 const T*
到 T*
的转换:const-correctness。
与 reverse_iterator
的比较无效,因为 "reverse iterator" 和 "forward iterator" 之间的区别与 "const iterator" 和 "non-const iterator" 之间的区别完全正交。后者有后果;前者最终没有。
Why does const_iterator not provide a const_iterator::base() function, to obtain corresponding non-const iterator like reverse_iterator does?
维护常量安全。提供这样的功能将是非常危险的,正如这里已经详细讨论过的那样。
At the end, I want to change the points, containing into resulting structure (say, shift them). But equally I want to protect them from modyfining during algorithm action, and therefore I used std::cbegin/std::cend as opposite to std::begin/std::end. Finally I have only const_iterator references to source points.
好吧,你对 base
成员的要求是错误的。当然它会解决你的问题,但正如所说,它太危险了。我再问你一个问题:
If I have a const_iterator
to an object and non-const access to the container, how do I efficiently (in constant time) get an iterator
to the referred object?
这里有一个巧妙的技巧可以做到这一点:
template <typename Container, typename ConstIterator>
typename Container::iterator remove_constness(Container& c, ConstIterator it)
{
return c.erase(it, it);
}
我不认为这个技巧有任何功劳。我从 and they credit Howard Hinnant and Jon Kalb
找到的
正如该答案的评论中所讨论的,此技巧适用于所有标准容器,但不一定适用于所有可能的符合标准的第三方容器,因为它们不需要提供 erase
。
就个人而言,我更希望标准容器有一个非常量成员函数,可以将给定的 const_iterator
转换为 iterator
,但它们没有,所以你需要变通它。
为什么const_iterator
不提供const_iterator::base()
函数,像reverse_iterator
那样获取相应的非常量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()
-ing 之后,interesting_subset
包含 universe
成员子集的迭代器。 algorithm()
从 interesting_subset
构造结果 structure
,它包含对 universe
.
最后,我想将包含的 point
s 更改为结果 structure
(例如,移动它们)。但同样地,我想在 algorithm
操作期间保护它们免受修改,因此我使用 std::cbegin
/std::cend
与 std::begin
/std::end
相反。最后我只有 const_iterator
对来源 point
s.
这是 iterator std::container< T >::const_iterator::base() const
我希望出现在 STL 容器中的成员函数的一个非常用例。
这是个有趣的问题。您希望能够在对它们进行推理时保护这些点,但是 return 对它们进行推理后对它们的可变引用。
作为对这些点进行推理的结果,您实际上 returning 是他们的 'names' 或 'identifiers'。
我们可以想像地按名称映射它们,并得到 sieve()
到 return 相关名称的向量。如果我们想避免存储正式名称(唯一编号、文本字符串等)的开销,这样的名称可以只是一个地址。
如果我们使用 const 对象的地址作为名称,那么当然要将其转回对可变对象的引用,我们需要一个 const_cast
。这可能被视为我们可以合理地使用 const 转换的少数几次之一。这样做时,我们应该将其封装在实用程序中 class 以限制任何影响。
编辑:重新考虑解决方案。这个现在不能被不守规矩的客户端代码滥用。
#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 )
A reverse_iterator
不会改变底层对象是否为 const 并且 const_iterator 与迭代器无关(除了可转换和引用的要求)所以你是比较苹果和橘子。
如果 const_iterator
确实可以通过 base
提供对非 const 迭代器的访问,则可以执行类似
auto const & a = std::vector<int>(20, 1);
auto it = std::cbegin(a);
*it.base() = 4;
标准允许这样做。
您确实 - 原则上 - 想要规避 const_iterator
提供的修改保护。但这就是 const_iterator
的意义所在。所以这既不是一个好主意,也不太可能发生,我认为(并希望)。
尽管我认为这是 XY Problem,但我只是回答了问题,而不是提供有关如何正确操作的指导。
-编辑-
如果你想让 const_iterator
能够 return 和 iterator
,你想要的是 iterator
。
你的意图似乎是
- 将
const_iterator
传递给sieve
,其中sieve
不允许更改元素。 - 将完全相同的迭代器传递给
algorithm
允许它修改它们。
您需要从同一个对象中获得两种不同的东西。
没有人阻止 sieve
的实现者使用 it.base()
,因此根本无法保证 sieve
不会更改元素。我再说一遍,这就是问题的重点 const_iterator
。
如果有任何方法可以使用 const_iterator
s 改变事物,它只会破坏它们。
出于同样的原因,没有从 const T*
到 T*
的转换:const-correctness。
与 reverse_iterator
的比较无效,因为 "reverse iterator" 和 "forward iterator" 之间的区别与 "const iterator" 和 "non-const iterator" 之间的区别完全正交。后者有后果;前者最终没有。
Why does const_iterator not provide a const_iterator::base() function, to obtain corresponding non-const iterator like reverse_iterator does?
维护常量安全。提供这样的功能将是非常危险的,正如这里已经详细讨论过的那样。
At the end, I want to change the points, containing into resulting structure (say, shift them). But equally I want to protect them from modyfining during algorithm action, and therefore I used std::cbegin/std::cend as opposite to std::begin/std::end. Finally I have only const_iterator references to source points.
好吧,你对 base
成员的要求是错误的。当然它会解决你的问题,但正如所说,它太危险了。我再问你一个问题:
If I have a
const_iterator
to an object and non-const access to the container, how do I efficiently (in constant time) get aniterator
to the referred object?
这里有一个巧妙的技巧可以做到这一点:
template <typename Container, typename ConstIterator>
typename Container::iterator remove_constness(Container& c, ConstIterator it)
{
return c.erase(it, it);
}
我不认为这个技巧有任何功劳。我从 and they credit Howard Hinnant and Jon Kalb
找到的正如该答案的评论中所讨论的,此技巧适用于所有标准容器,但不一定适用于所有可能的符合标准的第三方容器,因为它们不需要提供 erase
。
就个人而言,我更希望标准容器有一个非常量成员函数,可以将给定的 const_iterator
转换为 iterator
,但它们没有,所以你需要变通它。