 |
C++Talk.NET C++ language newsgroups
|
| View previous topic :: View next topic |
| Author |
Message |
James Kanze Guest
|
Posted: Wed Apr 25, 2007 10:51 am Post subject: Constraints on value type of iterators in two iterator ctors |
|
|
In response to a recent posting in comp.lang.c++, Gennaro Proto
raised a question as to the requirements concerning the value
type of the iterator used to instantiate the two iterator form
of the constructor. The actual example was:
unsigned char buffer[ N ] ;
// Fill buffer...
std::string s( buffer, buffer + N ) ;
This works with all implementations I have tried, but his
question was: is it guaranteed, and regretfully, I could find no
definitive answer in the standard. The section on this
constructor in the definition of std::basic_string forward to
the sequence requirements ([sequence.reqmts]), where all that is
said is that "X(i,j)" has a post condition that "isze() ==
distance between i and j", and that it "constructs a sequence
equal to the range [i,j)"; previous text constrains i and j to
satisfy input iterator requirements, and to be a valid range.
Obviously, buffer, buffer + N, above, meets all those
requirements. But...
1. Shouldn't there be at least the requirement that the
iterator value type be convertible to the value type of the
container? As written, things like:
void * a[] = { &x, &y, &z } ;
std::string s( a, a + 3 ) ;
or:
class C {
C( int ) ;
} ;
C a[] = { C(1), C(2), C(3) } ;
std::string s( a, a + 3 ) ;
would not seem to be forbidden.
2. What is actually meant by "constructs a sequence equal to"?
Consider:
double d[] = { 3.14, -0.1 } ;
std::string s( d, d + 2 ) ;
Is this legal? If not, why not? But if so, if I read the
text naïvely, the implementation is required to ensure
that
std::equals( s.begin(), s.end(), d )
returns true, which is going to be more than just difficult.
I would propose the following modifications:
In [sequence.reqmts]:
1. In paragraph 3, the text concerning i and j be amended to
read:
i and j denote iterators of the same type satisfying
input iterator requirements, [i, j) denotes a valid
range, and the value type of i and j be implicitly
convertible to the value type of the container
2. In the entry concerning X(i, j) in table 82, the third
column be changed to:
post size() == distance between i and j
constructs a sequence such that each element of the
sequence is equal to the corresponding element of
the sequence [i, j), converted to the value type of
the container.
3. In the entries concerning a.insert(p,i,j) and
a.assign(i,j), the text "copies of elements in [i,j)" be
replaced by "elements equal to the elements which would
be present in X(i,j)".
In [associative.requmts]:
1. In paragraph 7, the text concerning i and j be changed to:
i and j denote iterators of the same type satisfying
input iterator requirements, [i, j) denotes a valid
range, and the value type of i and j be convertible
to the value type of the container
(Note that the current text requires that the iterators
"refer to elemens of value_type". This is overly
constraining, as it is sufficient that the be implicitly
convertible to value_type. It is, in fact, a frequent idiom
in my own code to initialize a const map with something
like:
typedef std::map< std::string, int > Map ;
struct MapInit
{
char const* key ;
int value ;
operator Map::value_type() const
{
return Map::value_type( std::string( key ), value ) ;
}
} ;
MapInit const init[] =
{
{ "one", 1 },
{ "two", 2 },
{ "five", 5 } ,
} ;
Map m( init, init + 3 ) ;
It seems reasonable to me that this be supported.)
Interestingly enough, the text concerning the constructor in
table 84 already seems to allow for implicit conversion, since
an "element" can effectively be inserted into the container any
time it is convertible to value type.
In [unord.req]:
1. In paragraph 9, the same changes as in
[associative.requmts], paragraph 7, above.
--
James Kanze (GABI Software) mailto:james.kanze (AT) gmail (DOT) com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
---
[ 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 ] |
|
| Back to top |
|
 |
Greg Herlihy Guest
|
Posted: Thu Apr 26, 2007 10:16 pm Post subject: Re: Constraints on value type of iterators in two iterator c |
|
|
On 4/25/07 3:51 AM, in article
1177493160.051591.251220 (AT) n15g2000prd (DOT) googlegroups.com, "James Kanze"
<james.kanze (AT) gmail (DOT) com> wrote:
| Quote: | In response to a recent posting in comp.lang.c++, Gennaro Proto
raised a question as to the requirements concerning the value
type of the iterator used to instantiate the two iterator form
of the constructor. The actual example was:
unsigned char buffer[ N ] ;
// Fill buffer...
std::string s( buffer, buffer + N ) ;
This works with all implementations I have tried, but his
question was: is it guaranteed, and regretfully, I could find no
definitive answer in the standard. The section on this
constructor in the definition of std::basic_string forward to
the sequence requirements ([sequence.reqmts]), where all that is
said is that "X(i,j)" has a post condition that "isze() => distance between i and j", and that it "constructs a sequence
equal to the range [i,j)"; previous text constrains i and j to
satisfy input iterator requirements, and to be a valid range.
Obviously, buffer, buffer + N, above, meets all those
requirements. But...
1. Shouldn't there be at least the requirement that the
iterator value type be convertible to the value type of the
container? As written, things like:
void * a[] = { &x, &y, &z } ;
std::string s( a, a + 3 ) ;
or:
class C {
C( int ) ;
} ;
C a[] = { C(1), C(2), C(3) } ;
std::string s( a, a + 3 ) ;
would not seem to be forbidden.
|
Neither construction of "s" is forbidden - though both are likely to cause a
compiler error. According to the requirements for a Input Iterator (as
listed in table 89 - all references are to n2134), an Input Iterator for a
type T - upon dereferencing - must return an object that is, at the very
least, convertible to T. Since neither a pointer-to-void nor the
user-defined "C" class in the example above is convertible to the
container's value_type (char), then in neither case has the caller provided
a suitable Input Iterator type to construct the object.
Now, an argument that fails to meet the Standard's requirements for a Input
Iterator type - does not necessarily fail to construct the object. For one
reason, a container will fall back to its other constructors in such a
situation - and check whether any of its other constructors are able to
accept the arguments as provided. And for another, more important reason,
the Standard allows an implementation to decide on its own what an acceptal
Input Iterator type is - and therefore to accept (practically) any kind of
argument type as a qualifying input iterator, if the implementation so
chooses:
"The extent to which an implementation determines that a type cannot be an
input iterator is unspecified, except that as a minimum integral types shall
not qualify as input iterators." [§23.1.1/11]
| Quote: | 2. What is actually meant by "constructs a sequence equal to"?
Consider:
double d[] = { 3.14, -0.1 } ;
std::string s( d, d + 2 ) ;
Is this legal? If not, why not? But if so, if I read the
text naïvely, the implementation is required to ensure
that
std::equals( s.begin(), s.end(), d )
returns true, which is going to be more than just difficult.
|
Not really. This code:
double d[] = { 3.14, -0.1 } ;
std::string s( d, d + 2 ) ;
bool result = equal( s.begin(), s.end(), d, std::equal_to<char>());
std::cout << "d == s is " << std::boolalpha << result << "\n";
produces this output:
d == s is true
Meaning that the initializing values and the initial values in this example
are equal - once the types have been "normalized" to match each other and
the the type of item stored in the container.
| Quote: | I would propose the following modifications:
In [sequence.reqmts]:
1. In paragraph 3, the text concerning i and j be amended to
read:
i and j denote iterators of the same type satisfying
input iterator requirements, [i, j) denotes a valid
range, and the value type of i and j be implicitly
convertible to the value type of the container
|
The current requirement is actually "convertible" not "implicitly
convertible" so some ground would be lost with this change.
| Quote: | 2. In the entry concerning X(i, j) in table 82, the third
column be changed to:
post size() == distance between i and j
constructs a sequence such that each element of the
sequence is equal to the corresponding element of
the sequence [i, j), converted to the value type of
the container.
|
The reworded requirement seems to be too restrictive - it explicitly
requires that the source and container value types be convertible in at
least one direction - even though the actual requirement is that the two
types merely be equality-comparable. After all, in C++ it is possible to
test whether two objects of different types are equal - even if it is not
possible to convert between them.
| Quote: | 3. In the entries concerning a.insert(p,i,j) and
a.assign(i,j), the text "copies of elements in [i,j)" be
replaced by "elements equal to the elements which would
be present in X(i,j)".
|
The subjective expression is never explicitly resolved - so it leaves the
reader in a state of suspense: "the elements which would be present in
X(I,j)...if only - if only what exactly? I eventually realized that the past
tense was the problem - the construction of X(I,J) is actually meant to be
contemporaneous and hypothetical, not past and contrary-to-fact. So the
paragraph is stating that insertion is the same as construction when it
comes to Standard containers. Yet the Standard already goes on in some
length on this topic (starting at §21.1.1/9 and following) that additional
wording seems unneeded here.
| Quote: | In [associative.requmts]:
1. In paragraph 7, the text concerning i and j be changed to:
i and j denote iterators of the same type satisfying
input iterator requirements, [i, j) denotes a valid
range, and the value type of i and j be convertible
to the value type of the container
(Note that the current text requires that the iterators
"refer to elemens of value_type". This is overly
constraining, as it is sufficient that the be implicitly
convertible to value_type. It is, in fact, a frequent idiom
in my own code to initialize a const map with something
like:
typedef std::map< std::string, int > Map ;
struct MapInit
{
char const* key ;
int value ;
operator Map::value_type() const
{
return Map::value_type( std::string( key ), value ) ;
}
} ;
MapInit const init[] > {
{ "one", 1 },
{ "two", 2 },
{ "five", 5 } ,
} ;
Map m( init, init + 3 ) ;
It seems reasonable to me that this be supported.)
|
But a std::pair<char *, int> is not convertible (implicitly or otherwise) to
std::pair<std::string, int> - which is the map's value_type in the above
program. So even if you get want you wish for - and the the Standard did
adopt your proposed change - you would still not get what you want in terms
of this program.
| Quote: | Interestingly enough, the text concerning the constructor in
table 84 already seems to allow for implicit conversion, since
an "element" can effectively be inserted into the container any
time it is convertible to value type.
|
Yes, objects that are convertible to the associative container's value_type
may be inserted on an individual basis into the container - but only objects
of value_type proper may be inserted en masse by passing a range of
iterators. Perhaps the differing requirements represents a compromise.
Greg
---
[ 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 ] |
|
| Back to top |
|
 |
Daniel Krügler Guest
|
Posted: Sun Apr 29, 2007 5:58 pm Post subject: Re: Constraints on value type of iterators in two iterator c |
|
|
===================================== MODERATOR'S COMMENT:
There seems to be a problem with the moderation
system; this message was approved when first submitted.
===================================== END OF MODERATOR'S COMMENT
[To the mods: I posted this answer more than two days ago,
but even after the receive message it did not came through.
So this is the attempt of a retrial]
James Kanze schrieb:
| Quote: | 1. Shouldn't there be at least the requirement that the
iterator value type be convertible to the value type of the
container? As written, things like:
void * a[] = { &x, &y, &z } ;
std::string s( a, a + 3 ) ;
or:
class C {
C( int ) ;
} ;
C a[] = { C(1), C(2), C(3) } ;
std::string s( a, a + 3 ) ;
would not seem to be forbidden.
|
I strongly agree that the source items should be convertible
to those of the destination container. I also think that this is
currently not well expressed. IMO the sentence from 23.1.1/10
"[..] If there is no conversion from N to X::value_type, then this
is not a valid expression at all."
gives some evidence for this intent . Also note that n2134 has
made this *sligthly* clearer in its corresponding paragraph:
"In the previous paragraph the alternative binding will fail if first
is not implicitly convertible to X::size_type or if last is not
implicitly convertible to X::value_type."
Both wordings seem *not* to unambigiously express this intent.
| Quote: | 2. What is actually meant by "constructs a sequence equal to"?
Consider:
double d[] = { 3.14, -0.1 } ;
std::string s( d, d + 2 ) ;
Is this legal? If not, why not? But if so, if I read the
text naïvely, the implementation is required to ensure
that
std::equals( s.begin(), s.end(), d )
returns true, which is going to be more than just difficult.
|
I agree with that point.
| Quote: | I would propose the following modifications:
In [sequence.reqmts]:
1. In paragraph 3, the text concerning i and j be amended to
read:
i and j denote iterators of the same type satisfying
input iterator requirements, [i, j) denotes a valid
range, and the value type of i and j be implicitly
convertible to the value type of the container
|
Basically I think this is the right tone. Please note that
by means of concepts this is nicely expressed already
in
http://www2.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2085.pdf
page 8, 24.1.1/5:
concept Sequence<typename X> : Container<X>
{
...
X::X(size_type n, value_type t);
template<InputIterator Iter>
where Convertible<Iter::value_type, value_type>
X::X(Iter first, Iter last);
...
}
(Note that most recently "where" was changed to "requires")
and according to
http://www2.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2082.pdf
page 4, 20.1.8/1:
"Concept Convertible requires an implicit conversion from
one type to another.[..]"
| Quote: | 2. In the entry concerning X(i, j) in table 82, the third
column be changed to:
post size() == distance between i and j
constructs a sequence such that each element of the
sequence is equal to the corresponding element of
the sequence [i, j), converted to the value type of
the container.
3. In the entries concerning a.insert(p,i,j) and
a.assign(i,j), the text "copies of elements in [i,j)" be
replaced by "elements equal to the elements which would
be present in X(i,j)".
|
This would match the current demands on == for the
containers itself. Personally I have some provisios for the
current meaning of EqualityComparable from table 28,
20.1.1, where we have
For all a, a == a.
Question: Do we have to interpret floating point types as
types not necessarily fulfilling EqualityComparable,
because in case of NaN values a == a might not succeed?
I tend to say that if
std::numeric_limits<T>::has_quiet_NaN ||
std::numeric_limits<T>::has_signaling_NaN
then EqualityComparable is not necessarily fulfilled for
T (e.g. T = double).
Even apart from floating point types we can think of an
user-defined type T which might not provide an equivalence
relation. Table 65 in 23.1 is not very enligthening, it just
says for a == b:
"==is an equivalence relation."
But for what? I read it to mean an equivalence relation for
the containers, but what if T does not fulfill this? Interestingly
the corresponding preconditions for a < b are clearer
expressed:
"pre: < is defined for values of T. < is a total ordering relation."
On the other side this description seems to have *another*
inconsistency: According to the definition of
LessThanComparable, 20.1.2, we only need to fulfill a
*strict weak ordering* relation. So why must < be here a
*total ordering*?
Of course this is another problem than you want to discuss,
but IMO it exists. If so, we cannot *unconditionally* require
the above demanded equality statement as post-condition
of a sequence assignment with another sequence, we can
*only* require it, if there exists a equivalence relation
between the source type and the destination type.
(inset: The concept paper
http://www2.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2082.pdf
fortunately defines EqualityComparable in a heterogenous way as
auto concept EqualityComparable<typename T, typename U = T>.)
So what do we do, if this requirement is not fulfilled? The only
fallback I see is similar to the chicken-and-egg problem of
CopyConstructible, which ends with the statement "t is equivalent
to T(t)" with whatever interpretation we give for equivalent
Astonishingly the concept proposal has removed this requirement
on CopyConstructible - an oversight?
Greetings from Bremen,
Daniel
---
[ 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 ] |
|
| Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
|