这是我在comp.lang.c++.moderated上的提问。
还有其它人的回答:
tonci.tomic
instead of B) you can write:
template <class T>
T::iterator checked_erase(T& container, T::iterator position) {
if(position == container.end())
return container.end();
return container.erase(position);
}
so A is better because it is smaller, faster and standard.
llewelly
A good debugging implementation would assert if you passed it a
non-dereferenceable iterator. But since niether of these do that,
I don't see a convincing argument to prefer either.
Ulrich Eckhardt
The first. Giving an iterator to a non-existant object is a
programming-error. This includes passing end() just as well as passing
iterators to other containers or default-constructed ones. The check in
the second version only catches a small subset of them. Also, ignoring
programming-errors is not good. It should rather assert.
If you want to make it right, do it as STLport does it. Supply a special
debug-version where every iterator knows its container. That way, you can
find all faulty iterators and hasdle them accordingly.
Let me confess that things like
list.erase( find( list.begin(), list.end(), foo));
are tempting. However, I would not want to pay the extra check in cases
where I dont need it and there are things like remove() that can do the
job.
Michiel Salters
Better for what purpose? For an STL implementation, the first is more
efficient. The second turns a bug in user code into silent behavior.
I'd expect (in debug mode) that if this bug is detected, it is
signalled not hidden.
In a user library, for systems that should continue even in the
presence of bugs, I'd expect at least a call to an error logger.
However, a user library may of course be designed with this behavior
intended - but then we'd need to see the entire design to judge that.
基本上就是我说的意思:按照契约(contract)办事,各司其职,保证参数的正确性是调用者的责任,而函数的责任就是根据假定参数的正确并以此为基础进行计算。
这个老外的态度还是很鲜明的,看看这段话你就明白了:
The best approach is to have a clean contract and to implement
it. If enforcement of *that* contract can be done without hurting
performance too much, that is a good thing, but to silently allow
*some* erroneous uses as if they were valid doesn't do much to
help to make for solid code.
其中他特别说到,容错性太好的函数反而把错误给隐藏了(silently allow),以至于看上去是正常有效的,这样的做法对建立坚固的代码(solid code)没有什么帮助。
别人教我了,但是看不懂。如何翻译?谢谢!
> I just want to say: look at the following code, which is better, and why?
>
> A)
> iterator erase(iterator position) {
> link_type next_node = link_type(position.node->next);
> link_type prev_node = link_type(position.node->prev);
> prev_node->next = next_node;
> next_node->prev = prev_node;
> destroy_node(position.node);
> return iterator(next_node);
> }
>
> B)
> iterator erase(iterator position) {
>
> if (position == end())
> return end();
>
> link_type next_node = link_type(position.node->next);
> link_type prev_node = link_type(position.node->prev);
> prev_node->next = next_node;
> next_node->prev = prev_node;
> destroy_node(position.node);
> return iterator(next_node);
> }
Neither is completely better than the other. The first has
the advantage of being a more direct implementation of the
required semantics; it is simpler, and simplicity is a virtue.
The second catches one erroneous use out of many, adds execution
time to all proper uses, and (a) can tempt users to rely on the
undefined behavior as well as (b) discouraging users from using
functions as defined by their contracts. There's a lot of overlap
between (a) and (b), but the upshot is that code that relies on
the behavior of the second implementation is not portable C++.
Better would be something from a range-checking implementation
with a debug mode that asserted that 'position' is a dereferencable
iterator into the relevant container, so that the caller's error
can be flagged and corrected. I don't know offhand if STLPort's
debugging mode does this, but I wouldn't be surprised if it does
something similar.
It's *not* possible to implement a function so that it "works"
all the time when the caller doesn't use it correctly. It is
possible, at some cost in performance, to design programming
environments that can catch more errors than can C++, but they
can't detect most logic errors.
The best approach is to have a clean contract and to implement
it. If enforcement of *that* contract can be done without hurting
performance too much, that is a good thing, but to silently allow
*some* erroneous uses as if they were valid doesn't do much to
help to make for solid code.
This soon leads into discussions of what is The One True Thing To
Do when you know that the currently running program is in an
invalid state because of a bug (which is usually about all you
do know if an assertion fails). For many situations, exiting
(maybe with the option to dump data to a safe area) is the best
and safest thing to do here; on rare occasions it might be better
to stumble on, but I don't think I've come across such a situation.
其实我的意思是说,向borland C++ complier中的做法是不是比其他的做法会好写呢?
to oopig(面向对象的猪):
1)加上下面这段代码对性能应该影响不会太大吧。
if (position == end())
return end();
2)如果说“保证参数的正确性是调用者的责任”,那系统中是不是可以把大多数的向1)这样的判断都去掉呢?如否,那什么时候该判断呢?什么时候不用判断呢?如果从契约式的编程思想解释这个问题,那也该加上assert嘛。这对性能应该也不会影响太大吧。