将 const 容器引用作为参数传递时如何逃脱 const_iterator 陷阱
我通常更喜欢 constness,但最近遇到了 const 迭代器的难题,它动摇了我的 const 态度 让我对它们感到恼火:
MyList::const_iterator find( const MyList & list, int identifier )
{
// do some stuff to find identifier
return retConstItor; // has to be const_iterator because list is const
}
当然,我在这里试图表达的想法是,传入的list 不能/不会更改,但是一旦我将列表引用设置为 const,我就必须使用 'const_iterator',这会阻止我 做任何事情 修改结果(这是有道理的)。
那么,解决方案是放弃将传入的容器引用设为常量,还是我错过了另一种可能性?
这一直是我对 const 的秘密保留:即使你正确使用它,它也会在没有好的/干净的解决方案的情况下产生不应该出现的问题,尽管我认识到更具体地说,这是 const 和 const 之间的问题迭代器概念。
编辑:我非常清楚为什么你不能也不应该为 const 容器返回一个非常量迭代器。我的问题是,虽然我想要对通过引用传入的容器进行编译时检查,但我仍然想找到一种方法来传回某些内容的位置,并使用它来修改列表的非常量版本。正如其中一个答案中提到的,可以通过“提前”提取位置的概念,但很混乱/效率低下。
I generally prefer constness, but recently came across a conundrum with const iterators that shakes my const attitude annoys me about them:
MyList::const_iterator find( const MyList & list, int identifier )
{
// do some stuff to find identifier
return retConstItor; // has to be const_iterator because list is const
}
The idea that I'm trying to express here, of course, is that the passed in list cannot/willnot be changed, but once I make the list reference const I then have to use 'const_iterator's which then prevent me from doing anything with modifing the result (which makes sense).
Is the solution, then, to give up on making the passed in container reference const, or am I missing another possibility?
This has always been my secret reservation about const: That even if you use it correctly, it can create issues that it shouldn't where there is no good/clean solution, though I recognize that this is more specifically an issue between const and the iterator concept.
Edit: I am very aware of why you cannot and should not return a non-const iterator for a const container. My issue is that while I want a compile-time check for my container which is passed in by reference, I still want to find a way to pass back the position of something, and use it to modify the non-const version of the list. As mentioned in one of the answers it's possible to extract this concept of position via "advance", but messy/inefficient.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(8)
如果我正确理解您的意思,您正在尝试使用 const 向调用者表明您的函数不会修改集合,但您希望调用者(可能有一个非对集合的
const
引用),以便能够使用返回的迭代器修改集合。如果是这样,我认为没有一个干净的解决方案,除非容器提供一种将const
交互器转换为非const
交互器的机制(我是不知道有一个容器可以做到这一点)。您最好的选择可能是让您的函数采用非 const 引用。您还可以对函数进行 2 个重载,一个const
和一个非const
,这样在调用者只有一个的情况下const
引用,他们仍然能够使用你的函数。If I understand what you're saying correctly, you're trying to use
const
to indicate to the caller that your function will not modify the collection, but you want the caller (who may have a non-const
reference to the collection) to be able to modify the collection using the iterator you return. If so, I don't think there's a clean solution for that, unless the container provides a mechanism for turning aconst
interator into a non-const
one (I'm unaware of a container that does this). Your best bet is probably to have your function take a non-const
reference. You may also be able to have 2 overloads of your function, oneconst
and one non-const
, so that in the case of a caller who has only aconst
reference, they will still be able to use your function.这不是一个陷阱;而是一个陷阱。这是一个功能。 (:-)
一般来说,您不能从 const 方法返回非常量“句柄”到数据。例如,下面的代码是非法的。
如果它是合法的,那么你可以做这样的事情......
STL 的设计者遵循常量迭代器的这一约定。
就您而言, const 会给您带来在 const 集合上调用它的能力。在这种情况下,您不希望返回的迭代器是可修改的。但如果集合是非常量,您确实希望允许它可修改。因此,您可能需要两个接口:
或者,您可以使用一个模板函数完成所有操作。
如果输入迭代器是非常量,它将返回一个非常量迭代器,如果输入迭代器是常量,它将返回一个 const_iterator。
It's not a trap; it's a feature. (:-)
In general, you can't return a non-const "handle" to your data from a const method. For example, the following code is illegal.
If it were legal, then you could do something like this....
The designers of STL followed this convention with const-iterators.
In your case, what the const would buy you is the ability to call it on const collections. In which case, you wouldn't want the iterator returned to be modifiable. But you do want to allow it to be modifiable if the collection is non-const. So, you may want two interfaces:
Or, you can do it all with one template function
Then it will return a non-const iterator if the input iterators are non-const, and a const_iterator if they are const.
我对包装标准算法所做的工作是有一个元对象来确定容器的类型:
这允许提供单个实现,例如查找:
但是,这不允许使用临时变量调用它(我想我可以生活与它)。
无论如何,要返回对容器的可修改引用,显然您无法保证您的函数对容器做什么或不做什么。所以这个崇高的原则确实失效了:不要教条主义。
我认为 const 正确性更多的是为函数的调用者提供服务,而不是一些旨在确保您的简单查找函数正确的保姆措施。
另一个问题是:如果我定义了以下谓词,然后滥用标准 find_if 算法将所有值增加到第一个值 >= 3,你会有什么感觉:
(GCC 不会阻止我,但我无法告诉如果迂腐地说有一些未定义的行为。)
1)容器属于用户。由于允许通过谓词进行修改不会以任何方式损害算法,因此应该由调用者决定如何使用它。
2)这太可怕了!更好地实现 find_if 像这样,以避免这个噩梦(最好的办法,因为,显然,你不能选择迭代器是否是 const ):
What I've done with wrapping standard algorithms, is have a metaobject for determining the type of container:
This allows to provide a single implementation, e.g of find:
However, this doesn't allow calling this with temporaries (I suppose I can live with it).
In any case, to return a modifiable reference to the container, apparently you can't make any guarantees what your function does or doesn't do with the container. So this noble principle indeed breaks down: don't get dogmatic about it.
I suppose const correctness is more of a service for the caller of your functions, rather that some baby-sitting measure that is supposed to make sure you get your simple find function right.
Another question is: how would you feel if I defined a following predicate and then abused the standard find_if algorithm to increment all the values up to the first value >= 3:
(GCC doesn't stop me, but I couldn't tell if there's some undefined behaviour involved pedantically speaking.)
1) The container belongs to the user. Since allowing modification through the predicate in no way harms the algorithm, it should be up to the caller to decide how they use it.
2) This is hideous!!! Better implement find_if like this, to avoid this nightmare (best thing to do, since, apparently, you can't choose whether the iterator is const or not):
虽然我认为你的设计有点令人困惑(因为其他人指出迭代器允许在容器中进行更改,所以我不认为你的函数真的是 const),但有一种方法可以从 const_iterator 中获取迭代器。效率取决于迭代器的类型。
Although I think your design is a little confusing (as others have pointed iterators allow changes in the container, so I don't see your function really as const), there's a way to get an iterator out of a const_iterator. The efficiency depends on the kind of iterators.
在这种情况下,最好使用后置条件来保证列表,而不是尝试使用 const 关键字来保证列表不会被更改。换句话说,通过注释告诉用户该列表不会更改。
更好的是使用可以为迭代器或 const_iterators 实例化的模板:
当然,如果您要遇到那么多麻烦,您也可以将 MyList 的迭代器公开给用户并使用 std::find。
Rather than trying to guarantee that the list won't be changed using the const keyword, it is better in this case to guarantee it using a postcondition. In other words, tell the user via comments that the list won't be changed.
Even better would be using a template that could be instantiated for iterators or const_iterators:
Of course, if you're going to go to that much trouble, you might as well expose the iterators of MyList to the user and use std::find.
如果您要更改迭代器指示的数据,那么您就是在更改列表。
什么是“东什么”?修改数据?那就是改变列表,这与你的初衷相矛盾。如果列表是常量,则它(并且“它”包括其数据)是常量。
如果你的函数要返回一个非常量迭代器,它将创建一种修改列表的方法,因此该列表不会是常量的。
If you're changing the data directed by the iterator, you're changing the list.
What is "dong anything"? Modifying the data? That's changing the list, which is contradictory to your original intentions. If a list is const, it (and "it" includes its data) is constant.
If your function were to return a non-const iterator, it would create a way of modifying the list, hence the list wouldn't be const.
您以错误的方式思考您的设计。不要使用 const 参数来指示函数的作用 - 使用它们来描述参数。在这种情况下,find() 不更改列表并不重要。重要的是 find() 旨在与可修改列表一起使用。
如果您希望 find() 函数返回非常量迭代器,那么它允许调用者修改容器。它接受 const 容器是错误,因为这将为调用者提供一种删除容器 const 性的隐藏方法。
考虑一下:
所以,你的函数应该接受一个非常量参数:
现在,当调用者尝试使用你的函数来修改她的常量列表时,她会得到一个编译错误。这是一个更好的结果。
You are thinking about your design in the wrong way. Don't use const arguments to indicate what the function does - use them to describe the argument. In this case, it doesn't matter that find() doesn't change the list. What matters is that find() is intended to be used with modifiable lists.
If you want your
find()
function to return a non-const iterator, then it enables the caller to modify the container. It would be wrong for it to accept a const container, because that would provide the caller with a hidden way of removing the const-ness of the container.Consider:
So, your function should take a non-const argument:
Now, when the caller tries to use your function to modify her const list, she'll get a compilation error. That's a much better outcome.
如果要向容器返回非常量访问器,请将该函数也设为非常量。您承认容器可能会因副作用而改变。
这是标准算法采用迭代器而不是容器的一个很好的理由,因此它们可以避免这个问题。
If you're going to return a non-const accessor to the container, make the function non-const as well. You're admitting the possibility of the container being changed by a side effect.
This is a good reason the standard algorithms take iterators rather than containers, so they can avoid this problem.