Re: Deprecate the use of plain pointers as standard container iterators
Nicola Musatti wrote:
First of all my examples may not be the best ones to show the problems
I'm writing about, but this doesn't mean that the problems aren't
there, so focusing too much on the details of my code is really missing
the issue I was trying to point out.
Yes, but in order to show that there really is a problem that calls for
a change to the standard, it's helpful to provide an example where the
problem that's being demonstrated is not easily fixed by methods that
don't involve changing the standard.
kuyper@wizard.net wrote:
Nicola Musatti wrote:
[...]
void f() {
std::vector<int> v;
//...
std::sort(++v.begin(), v.end());
}
If std::vector<>::iterator is a pointer the expression ++v.begin() is
invalid because it attempts to modify an rvalue.
Which opens the question: why does it try to modify an rvalue? Why not
use v.begin()+1 instead? As written, this code pointlessly specifies an
additional operation that doesn't need to be performed, which is
harmless when that operation can be performed, but fails when it can't
be. I don't think this example provides strong support for your
argument. It does provide support for the idea that your code should
never specify any actions beyond those that actually need to be taken,
even if those extra actions seem harmless.
Change the std::vector above with std::list and your objection still
applies yet your suggestion doesn't anymore. The code above attempts to
modify an rvalue because it is reasonable to do so in order to write
clearer, more generic code. Compilers have all the information
available to produce efficient code.
Moreover your own comment is a strong indication that I'm right:
incrementing an iterator rvalue is OK when it is a class instance, but
not when it is a scalar temporary? This is the kind of pitfall that
makes C++ needlessly hard to learn and use.
That's an annoying long-term consequence of the C legacy. Scalar types
have different rules than class types, in ways that minimizes the
number of changes that are needed to make C code compile under C++
without change in semantics. Now that a great deal of C++ code has been
written relying on those same rules, backwards compatibility with C++
code ends up imposing the same requirement that was originally imposed
for backwards compatibility with C code. The only way to avoid such
problems completely would have been to abandon the goal of backwards
compatibility with C from the very beginning.
I might agree to the notion that it was a mistake to allow calling non
const member functions on rvalues, but that's too late to change now,
isn't it?
I don't agree that it was a mistake, but I do agree that it's too late.
The following code gives different results on different platforms:
int * find(int * b, int * e, int d) {
return b + d < e ? b + d : e;
}
//...
std::vector<int>::iterator i = find(v.begin(), v.end(), 2);
std::cout << ( i != v.end() ? *i : 0 ) << '\n';
If std::vector<>::iterator is a pointer the user defined find function
is chosen.
"Doctor, it hurts when I hit myself in the head with a hammer."
"Then stop hitting yourself in the head with a hammer."
This implies that I know it is a hammer. Unfortunately the C++ standard
doesn't tell me.
The C++ standard tells you what the semantics of std::find() are, and
how overload resolution is performed on a call to find(). If overload
resolution doesn't reliably identify an overload of find() with
acceptable semantics, you should be using different syntax for the call
to ensure that an acceptable overload is identified.
Use std::find(), if you want to make sure that it's actually
std::find() that you're calling. If you actually want the compiler to
consider other possible overloads, make sure that any other overload
that could be selected has acceptable semantics, and doesn't lead to
ambiguity as to which overload should be selected. This is the same
advice that applies whenever calling any function; there's nothing
special about the fact that v.begin() might (or might not) be a
pointer.
But the problem is exactly the fact that semantics are not sufficiently
specified!
I'm confused by that statement. There are three different semantics
that I think you could be referring to:
1. The semantics of std::find(), which strike me as being fairly well
specified.
2. The semantics you want to have executed when your code calls find()
- if that's not well specified, then you've got a serious problem, that
has nothing to do with the C++ language, and everything to do with an
inaquedately specific definition of what your code should do.
3. The semantics that actually will be executed when your code calls
find(). If these are the semantics that are insufficiently specified,
it implies that you don't know enough about which overload of find()
will be called, to be sure that the semantics of that overload are
acceptable. If that's the case, you need to change the syntax of your
function call to ensure that only overloads that are actually
acceptable will be chosen. For instance, if std::find() provides the
semantics you're looking for, then call it by using std::find() rather
than find().
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]