Discussion:
One more foolishness of the C++ Standard
(too old to reply)
Vladimir Grigoriev
2010-01-21 10:52:12 UTC
Permalink
Let consider an example.

There is a class named Person. And it has a member size_t personalID and an
equality operator

bool operator ==( const Person &lhs, size_t targetID );

However there is not a Person constructor with one argument equal to size_t
as a personal ID.
So there is the above equality operator and there is no a reverse equality
operator as

bool operator ==( size_t targetID, const Personal &rhs ); // invalid

Let assume further that some persons with personal IDs which belong to a
range of personal IDs (a simple array) have gotten a bonus. And we are going
to check which persons of some department are among them.

What should we to do? Shoot ourselves?
Our attempt to use the algorithm std::find_first_of is prohibited by the
Standard which requires equality comparability.
So main area of classes and objects are overboard of STD algorithms. At the
same time there is not something in the definition of find algorithms that
prevents to use them with classes which have an equality operator which does
not satisfies the requirements
if a == b then must be b == a

Vladimir Grigoriev
Victor Bazarov
2010-01-21 13:05:53 UTC
Permalink
Post by Vladimir Grigoriev
Let consider an example.
There is a class named Person. And it has a member size_t personalID and an
equality operator
bool operator ==( const Person &lhs, size_t targetID );
However there is not a Person constructor with one argument equal to size_t
as a personal ID.
So there is the above equality operator and there is no a reverse equality
operator as
bool operator ==( size_t targetID, const Personal &rhs ); // invalid
Let assume further that some persons with personal IDs which belong to a
range of personal IDs (a simple array) have gotten a bonus. And we are going
to check which persons of some department are among them.
What should we to do? Shoot ourselves?
Our attempt to use the algorithm std::find_first_of is prohibited by the
Standard which requires equality comparability.
Huh? Define a predicate that will take size_t and Personal and use "the
other" version of 'find_first_of'.
Post by Vladimir Grigoriev
So main area of classes and objects are overboard of STD algorithms. At the
same time there is not something in the definition of find algorithms that
prevents to use them with classes which have an equality operator which does
not satisfies the requirements
if a == b then must be b == a
You seem to be genuinely concerned with Standard's deficiencies. That's
very commendable. Now, collect all your gripes, make sure you don't
miss anything (like this time, for instance), and then make your
suggestions how to improve the language and/or the library. Follow the
procedure for filing defect reports, post to 'comp.std.c++', in other
words join in on the fun. Then your complaining will look more like
constructive criticism rather than whining of somebody who hasn't
learned the language yet. No offence intended.

V
--
Please remove capital 'A's when replying by e-mail
I do not respond to top-posted replies, please don't ask
Vladimir Grigoriev
2010-01-21 13:32:54 UTC
Permalink
Post by Victor Bazarov
Post by Vladimir Grigoriev
Let consider an example.
There is a class named Person. And it has a member size_t personalID and
an equality operator
bool operator ==( const Person &lhs, size_t targetID );
However there is not a Person constructor with one argument equal to
size_t as a personal ID.
So there is the above equality operator and there is no a reverse
equality operator as
bool operator ==( size_t targetID, const Personal &rhs ); // invalid
Let assume further that some persons with personal IDs which belong to a
range of personal IDs (a simple array) have gotten a bonus. And we are
going to check which persons of some department are among them.
What should we to do? Shoot ourselves?
Our attempt to use the algorithm std::find_first_of is prohibited by the
Standard which requires equality comparability.
Huh? Define a predicate that will take size_t and Personal and use "the
other" version of 'find_first_of'.
Victor, what predicate should I build instead of already existent equality
operator? In fact I must take for example as an alternative of the equality
operator the std::equal_to functor and change it such a way that nobody
even could notice that I am using the same equality operator instead of
using the equality operator directly. I do not see a great sense in this.

Vladimir Grigoriev
Vladimir Grigoriev
2010-01-21 13:42:31 UTC
Permalink
Victor it is funny that only now after your response I have seen that the
requirement
"Type T is EquallyComparable" exist only in the comments for the std::find
algorithm. Al other find algorithms (find_end, find_first_of) have no this
phrase in their comments. Is this requirement applicable to all find
algorithms or only for the first one?

Vladimir Grigoriev
Ulrich Eckhardt
2010-01-21 14:57:29 UTC
Permalink
Note up front: There are comp.lang.c++.moderated and comp.lang.std.c++
(IIRC) where standard defects are better taken care of. Bitching and
moaning about a standard and that others follow or even enforce the
standard is IMHO just noise _here_.
Post by Vladimir Grigoriev
There is a class named Person. And it has a member size_t personalID and
an equality operator
bool operator ==( const Person &lhs, size_t targetID );
However there is not a Person constructor with one argument equal to
size_t as a personal ID.
So there is the above equality operator and there is no a reverse equality
operator as
bool operator ==( size_t targetID, const Personal &rhs ); // invalid
I find that already questionable, I wouldn't overload operator== for that.
Why not overload comparison with a std::string to compare the name? And the
same again, to compare the address, etc. etc. etc.
Post by Vladimir Grigoriev
Let assume further that some persons with personal IDs which belong
to a range of personal IDs (a simple array) have gotten a bonus. And
we are going to check which persons of some department are among them.
What should we to do? Shoot ourselves?
Invoke std::find_if in a loop:

iterator it = begin;
while(true) {
it=find_if(begin, end, <predicate>);
if(it==end)
break;
it->give_raise();
++it;
}
Post by Vladimir Grigoriev
Our attempt to use the algorithm std::find_first_of is prohibited by the
Standard which requires equality comparability. So main area of classes
and objects are overboard of STD algorithms.
*sigh* No, you just need to learn how to use them. And yes, sometimes that
requires writing some code.

Uli
--
C++ FAQ: http://parashift.com/c++-faq-lite

Sator Laser GmbH
Geschäftsführer: Thorsten Föcking, Amtsgericht Hamburg HR B62 932
Vladimir Grigoriev
2010-01-21 15:22:24 UTC
Permalink
Ulrich, the details of the Person class realization is not important. It is
important that for this class there is no a great sense to introduce
equality operators which compare two person or which compare size_t value or
something else with the person object. However there is a great sense to
compare a person with some type of identificator.

Vladimir Grigoriev
Post by Ulrich Eckhardt
Note up front: There are comp.lang.c++.moderated and comp.lang.std.c++
(IIRC) where standard defects are better taken care of. Bitching and
moaning about a standard and that others follow or even enforce the
standard is IMHO just noise _here_.
Post by Vladimir Grigoriev
There is a class named Person. And it has a member size_t personalID and
an equality operator
bool operator ==( const Person &lhs, size_t targetID );
However there is not a Person constructor with one argument equal to
size_t as a personal ID.
So there is the above equality operator and there is no a reverse equality
operator as
bool operator ==( size_t targetID, const Personal &rhs ); // invalid
I find that already questionable, I wouldn't overload operator== for that.
Why not overload comparison with a std::string to compare the name? And the
same again, to compare the address, etc. etc. etc.
Post by Vladimir Grigoriev
Let assume further that some persons with personal IDs which belong
to a range of personal IDs (a simple array) have gotten a bonus. And
we are going to check which persons of some department are among them.
What should we to do? Shoot ourselves?
iterator it = begin;
while(true) {
it=find_if(begin, end, <predicate>);
if(it==end)
break;
it->give_raise();
++it;
}
Post by Vladimir Grigoriev
Our attempt to use the algorithm std::find_first_of is prohibited by the
Standard which requires equality comparability. So main area of classes
and objects are overboard of STD algorithms.
*sigh* No, you just need to learn how to use them. And yes, sometimes that
requires writing some code.
Uli
--
C++ FAQ: http://parashift.com/c++-faq-lite
Sator Laser GmbH
Geschaftsfuhrer: Thorsten Focking, Amtsgericht Hamburg HR B62 932
Martin B.
2010-01-21 15:18:22 UTC
Permalink
Post by Vladimir Grigoriev
Let consider an example.
There is a class named Person. And it has a member size_t personalID and an
equality operator
bool operator ==( const Person &lhs, size_t targetID );
However there is not a Person constructor with one argument equal to size_t
as a personal ID.
So there is the above equality operator and there is no a reverse equality
operator as
bool operator ==( size_t targetID, const Personal &rhs ); // invalid
Could you explain this to me? What is the actual problem?

This compiles just fine:
class Foo { };

bool operator==(Foo const& lhs, size_t rhs) {
return true;
}

bool operator==(size_t lhs, Foo const& rhs) {
return true;
}

void f() {
Foo o = Foo();
size_t i = size_t();

if( o == i) {
// x
}
else if(i == o) {
// y
}
else {
// z
}
}



cheers,
Martin
Vladimir Grigoriev
2010-01-21 15:31:38 UTC
Permalink
There is no a great sense to compare some size_t value with an object of
type Person. This comparison has no sense. It is a general situation when
converting some object of a class to an integral type has sense while
converting an integral type to an object has no sense. For this purpose the
explicit reywoard was introduced. The same is valid for operators. i.e.
sometimes a op b has sense while b op a has no sense.

Vladimir Grigoriev
Post by Martin B.
Post by Vladimir Grigoriev
Let consider an example.
There is a class named Person. And it has a member size_t personalID and
an equality operator
bool operator ==( const Person &lhs, size_t targetID );
However there is not a Person constructor with one argument equal to
size_t as a personal ID.
So there is the above equality operator and there is no a reverse
equality operator as
bool operator ==( size_t targetID, const Personal &rhs ); // invalid
Could you explain this to me? What is the actual problem?
class Foo { };
bool operator==(Foo const& lhs, size_t rhs) {
return true;
}
bool operator==(size_t lhs, Foo const& rhs) {
return true;
}
void f() {
Foo o = Foo();
size_t i = size_t();
if( o == i) {
// x
}
else if(i == o) {
// y
}
else {
// z
}
}
cheers,
Martin
Ulrich Eckhardt
2010-01-21 16:34:45 UTC
Permalink
Post by Vladimir Grigoriev
There is no a great sense to compare some size_t value with an object of
type Person. This comparison has no sense. It is a general situation when
converting some object of a class to an integral type has sense while
converting an integral type to an object has no sense. For this purpose
the explicit reywoard was introduced. The same is valid for operators.
i.e. sometimes a op b has sense while b op a has no sense.
I'd say whether switching operands still has sense depends on the operator.
For an equality comparison, I expect it to be fully commutative. Similarly
for inequality, while e.g. greater behaves slightly different.

Are you really implying that a==b makes sense when b==a doesn't? I already
voiced my opinion on comparison of a person with an ID: "I wouldn't
overload operator== for that."

Uli
--
C++ FAQ: http://parashift.com/c++-faq-lite

Sator Laser GmbH
Geschäftsführer: Thorsten Föcking, Amtsgericht Hamburg HR B62 932
Vladimir Grigoriev
2010-01-21 17:26:40 UTC
Permalink
There are situations when A is a subset of B and in this case you may apply
some relation or comparison operator showing that A is a subset of B.
However you may not state that B is a subset of A. So a reverse relation is
absent and consequently reverse operators also are absent.
For example you can define a Point class derived from std::pair. And your
Point class may have arithmetic operators. You may to add a std::pair object
to a Point object but your may not add a Point object to a std:pair object.
Otherwise std::pair class will have a feature which was not supposed to be.
The same situation can be with relation operators.

Vladimir Grigoriev
Post by Ulrich Eckhardt
Are you really implying that a==b makes sense when b==a doesn't? I already
voiced my opinion on comparison of a person with an ID: "I wouldn't
overload operator== for that."
Uli
--
C++ FAQ: http://parashift.com/c++-faq-lite
Sator Laser GmbH
Geschaftsfuhrer: Thorsten Focking, Amtsgericht Hamburg HR B62 932
Ulrich Eckhardt
2010-01-22 08:11:43 UTC
Permalink
Post by Vladimir Grigoriev
There are situations when A is a subset of B and in this case you may
apply some relation or comparison operator showing that A is a subset of
B. However you may not state that B is a subset of A. So a reverse
relation is absent and consequently reverse operators also are absent.
That is a violation of the principle of least surprise to overload operator=
then.
Post by Vladimir Grigoriev
For example you can define a Point class derived from std::pair. And your
Point class may have arithmetic operators. You may to add a std::pair
object to a Point object but your may not add a Point object to a std:pair
object.
Why not? If a+b computes then b+a should, too.

Curiosum btw:

float a[42];
0[a] = 3.1416f;

Though I would personally say this goes too far. ;)
Post by Vladimir Grigoriev
Otherwise std::pair class will have a feature which was not
supposed to be. The same situation can be with relation operators.
I disagree.

Uli
--
C++ FAQ: http://parashift.com/c++-faq-lite

Sator Laser GmbH
Geschäftsführer: Thorsten Föcking, Amtsgericht Hamburg HR B62 932
Vladimir Grigoriev
2010-01-22 12:29:40 UTC
Permalink
Post by Ulrich Eckhardt
Post by Vladimir Grigoriev
For example you can define a Point class derived from std::pair. And your
Point class may have arithmetic operators. You may to add a std::pair
object to a Point object but your may not add a Point object to a std:pair
object.
Why not? If a+b computes then b+a should, too.
Well, when try to build two template classes one derived from another such a
way that the base class will not get a new feature of the derived class.

For example

template <typename T>
class A
{
...
template <typename U>
A( const A<U> & );
...
};
template <typename T>
class B: public A<T>
{
...
template <typename U>
B( const B<U> & );
...
};

and class A for example has no a sum operation while class B has.
So the following should be valid

A a;
B b;

b + b;
b + a;
a + b;

however
a + a is not valid

Vladimir Grigoriev
Ulrich Eckhardt
2010-01-22 13:58:28 UTC
Permalink
Post by Vladimir Grigoriev
Post by Ulrich Eckhardt
Post by Vladimir Grigoriev
For example you can define a Point class derived from std::pair. And
your Point class may have arithmetic operators. You may to add a
std::pair object to a Point object but your may not add a Point object
to a std:pair
object.
Why not? If a+b computes then b+a should, too.
Well, when try to build two template classes one derived from another such
a way that the base class will not get a new feature of the derived class.
For example
template <typename T>
class A
{
...
template <typename U>
A( const A<U> & );
...
};
template <typename T>
class B: public A<T>
{
...
template <typename U>
B( const B<U> & );
...
};
and class A for example has no a sum operation while class B has.
So the following should be valid
A a;
B b;
b + b;
b + a;
a + b;
So, a+b computes just as well as b+a, just as I said it should.
Post by Vladimir Grigoriev
however
a + a is not valid
I never claimed it should.

Uli
--
C++ FAQ: http://parashift.com/c++-faq-lite

Sator Laser GmbH
Geschäftsführer: Thorsten Föcking, Amtsgericht Hamburg HR B62 932
Vladimir Grigoriev
2010-01-22 14:26:47 UTC
Permalink
Post by Ulrich Eckhardt
Post by Vladimir Grigoriev
Post by Ulrich Eckhardt
Post by Vladimir Grigoriev
For example you can define a Point class derived from std::pair. And
your Point class may have arithmetic operators. You may to add a
std::pair object to a Point object but your may not add a Point object
to a std:pair
object.
Why not? If a+b computes then b+a should, too.
Well, when try to build two template classes one derived from another such
a way that the base class will not get a new feature of the derived class.
For example
template <typename T>
class A
{
...
template <typename U>
A( const A<U> & );
...
};
template <typename T>
class B: public A<T>
{
...
template <typename U>
B( const B<U> & );
...
};
and class A for example has no a sum operation while class B has.
So the following should be valid
A a;
B b;
b + b;
b + a;
a + b;
So, a+b computes just as well as b+a, just as I said it should.
Post by Vladimir Grigoriev
however
a + a is not valid
I never claimed it should.
However try to provide that a + a will not compute when a + b, b + a, and b
+ b will compute for two classes when one is derived from another..

Vladimir Grigoriev
Ulrich Eckhardt
2010-01-25 08:42:25 UTC
Permalink
Post by Vladimir Grigoriev
However try to provide that a + a will not compute when a + b, b + a, and
b + b will compute for two classes when one is derived from another..
Simple, provide these three overloads and no others:

b operator+(a const& l, b const& r);
b operator+(b const& l, a const& r);
b operator+(b const& l, b const& r);

However, I can't think of a reason for this either. Think of this:

b some_b;
a& a_ref = some_b;
a_ref+a_ref; // now what?

struct c: b {...};
c some_c;
b& b_ref = some_c;
b_ref+b_ref; // and here?

Generally, operator+ is something that rather fits for value types. Public
inheritance usually means the type is rather an entity type, which don't
like being treated as a value.

Uli
--
C++ FAQ: http://parashift.com/c++-faq-lite

Sator Laser GmbH
Geschäftsführer: Thorsten Föcking, Amtsgericht Hamburg HR B62 932
Vladimir Grigoriev
2010-01-25 10:58:35 UTC
Permalink
Ulrich, I forgot to add that in the derived class the constructor for the
base class must be.

B( const A & );

In this case two conversions can be: B ==> A and A ==> B and an ambiguous
reference can occur.

Vladimir Grigoriev
.
Post by Ulrich Eckhardt
Post by Vladimir Grigoriev
However try to provide that a + a will not compute when a + b, b + a, and
b + b will compute for two classes when one is derived from another..
b operator+(a const& l, b const& r);
b operator+(b const& l, a const& r);
b operator+(b const& l, b const& r);
b some_b;
a& a_ref = some_b;
a_ref+a_ref; // now what?
struct c: b {...};
c some_c;
b& b_ref = some_c;
b_ref+b_ref; // and here?
Generally, operator+ is something that rather fits for value types. Public
inheritance usually means the type is rather an entity type, which don't
like being treated as a value.
Uli
--
C++ FAQ: http://parashift.com/c++-faq-lite
Sator Laser GmbH
Geschaftsfuhrer: Thorsten Focking, Amtsgericht Hamburg HR B62 932
Ulrich Eckhardt
2010-01-25 13:11:56 UTC
Permalink
Post by Vladimir Grigoriev
Ulrich, I forgot to add that in the derived class the constructor for the
base class must be.
B( const A & );
In this case two conversions can be: B ==> A and A ==> B and an ambiguous
reference can occur.
Make it explicit, or maybe don't use public derivation.
Post by Vladimir Grigoriev
Post by Ulrich Eckhardt
b some_b;
a& a_ref = some_b;
a_ref+a_ref; // now what?
struct c: b {...};
c some_c;
b& b_ref = some_c;
b_ref+b_ref; // and here?
Did you think about those cases? How should those two cases behave?

Again, please don't quote anything up to the signature, it's rude and it
makes communication much harder, as people have to guess what you're
referring to. Thank you.

Uli
--
C++ FAQ: http://parashift.com/c++-faq-lite

Sator Laser GmbH
Geschäftsführer: Thorsten Föcking, Amtsgericht Hamburg HR B62 932
Vladimir Grigoriev
2010-01-26 11:51:30 UTC
Permalink
If you will make it explicit you get another problem with declaring objects.
For example you can not write

A a;
B b = a;

And even you do not bother about this restriction adding template
constructor will destroy your set of operators.

Vladimir Grigoriev
Post by Ulrich Eckhardt
Post by Vladimir Grigoriev
Ulrich, I forgot to add that in the derived class the constructor for the
base class must be.
B( const A & );
In this case two conversions can be: B ==> A and A ==> B and an ambiguous
reference can occur.
Make it explicit, or maybe don't use public derivation.
Post by Vladimir Grigoriev
Post by Ulrich Eckhardt
b some_b;
a& a_ref = some_b;
a_ref+a_ref; // now what?
struct c: b {...};
c some_c;
b& b_ref = some_c;
b_ref+b_ref; // and here?
Did you think about those cases? How should those two cases behave?
Again, please don't quote anything up to the signature, it's rude and it
makes communication much harder, as people have to guess what you're
referring to. Thank you.
Uli
--
C++ FAQ: http://parashift.com/c++-faq-lite
Sator Laser GmbH
Geschaftsfuhrer: Thorsten Focking, Amtsgericht Hamburg HR B62 932
Duane Hebert
2010-01-26 13:17:18 UTC
Permalink
Post by Vladimir Grigoriev
If you will make it explicit you get another problem with declaring objects.
For example you can not write
A a;
B b = a;
But if you can do B b = a; what is the point of
having separate A and B objects?
Vladimir Grigoriev
2010-01-26 14:14:14 UTC
Permalink
Post by Duane Hebert
Post by Vladimir Grigoriev
If you will make it explicit you get another problem with declaring
objects. For example you can not write
A a;
B b = a;
But if you can do B b = a; what is the point of
having separate A and B objects?
The derived class adds new features relative to the base class. So when a
new B object is created it can use data of an existent A object.
Vladimir Grigoriev
Duane Hebert
2010-01-26 14:23:42 UTC
Permalink
Post by Vladimir Grigoriev
Post by Duane Hebert
Post by Vladimir Grigoriev
If you will make it explicit you get another problem with declaring
objects. For example you can not write
A a;
B b = a;
But if you can do B b = a; what is the point of
having separate A and B objects?
The derived class adds new features relative to the base class. So when a
new B object is created it can use data of an existent A object.
Do you know about slicing?
Vladimir Grigoriev
2010-01-26 15:38:53 UTC
Permalink
Why are you asking about this?

Vladimir Grigoriev
Post by Duane Hebert
Post by Vladimir Grigoriev
Post by Duane Hebert
Post by Vladimir Grigoriev
If you will make it explicit you get another problem with declaring
objects. For example you can not write
A a;
B b = a;
But if you can do B b = a; what is the point of
having separate A and B objects?
The derived class adds new features relative to the base class. So when a
new B object is created it can use data of an existent A object.
Do you know about slicing?
Duane Hebert
2010-01-26 16:09:31 UTC
Permalink
Post by Vladimir Grigoriev
Why are you asking about this?
Because I'm wondering how you expect
A a;
B b = a;

to work?

It looks like you're describing polymorphism here but
you don't use references or pointers so what would you
expect this to do? Saying B *b = new A; is not the same
as what you have here.

IMO if a B is not EXACTLY an A then this doesn't work.
If it is exactly an A, then either A or B is not needed.
If B is derived from A,it doesn't mean that they are equal. It just means that B includes
what A has. Or are you trying to initialize the "A" part of B here?

I'm just curious. I mean is A is a shape and B is a circle and you
do shape s; circle c = s; then you create a shape, create a circle and then
replace the circle with a shape. So it's no longer a circle.
Vladimir Grigoriev
2010-01-26 16:42:01 UTC
Permalink
Post by Duane Hebert
Post by Vladimir Grigoriev
Why are you asking about this?
Because I'm wondering how you expect
A a;
B b = a;
to work?
It looks like you're describing polymorphism here but
you don't use references or pointers so what would you expect this to do?
Saying B *b = new A; is not the same
as what you have here.
B *b = new A; Is it a valid code?
Post by Duane Hebert
IMO if a B is not EXACTLY an A then this doesn't work.
If it is exactly an A, then either A or B is not needed. If B is derived
from A,it doesn't mean that they are equal. It just means that B includes
what A has. Or are you trying to initialize the "A" part of B here?
I'm just curious. I mean is A is a shape and B is a circle and you do
shape s; circle c = s; then you create a shape, create a circle and then
replace the circle with a shape. So it's no longer a circle.
Vladimir Grigoriev
Duane Hebert
2010-01-26 17:04:48 UTC
Permalink
Post by Vladimir Grigoriev
Post by Duane Hebert
Post by Vladimir Grigoriev
Why are you asking about this?
Because I'm wondering how you expect
A a;
B b = a;
to work?
It looks like you're describing polymorphism here but
you don't use references or pointers so what would you expect this to do?
Saying B *b = new A; is not the same
as what you have here.
B *b = new A; Is it a valid code?
If A is derived from B you can use a base class
pointer to a derived class.

If B is derived from A then it wouldn't compile but
then what do you think B b = a would (or should) do?

I really don't understand your point.
Vladimir Grigoriev
2010-01-26 17:23:25 UTC
Permalink
My example was

A{};
B: public A {};

A a;
B b = a;

If there is a constructor in the B class

explicit B( const A & )

then
B b = a;

will not be allowed.

Consider a class

template <typename T>
class Point : public std::pair<T, T>
{
T &x;
T &y;
template <typename U, typename V>
Point( const std::pair<U, V> & );
...
};

and try to write for it three operators

Point + Point;
Point + std::pair;
std::pair + Point

such a way that

std::pair + std::pair

will be impossible.

Vladimir Grigoriev
Post by Duane Hebert
Post by Vladimir Grigoriev
Post by Duane Hebert
Post by Vladimir Grigoriev
Why are you asking about this?
Because I'm wondering how you expect
A a;
B b = a;
to work?
It looks like you're describing polymorphism here but
you don't use references or pointers so what would you expect this to
do? Saying B *b = new A; is not the same
as what you have here.
B *b = new A; Is it a valid code?
If A is derived from B you can use a base class
pointer to a derived class.
If B is derived from A then it wouldn't compile but
then what do you think B b = a would (or should) do?
I really don't understand your point.
Igor Tandetnik
2010-01-26 17:38:10 UTC
Permalink
Post by Vladimir Grigoriev
My example was
A{};
B: public A {};
A a;
B b = a;
If there is a constructor in the B class
explicit B( const A & )
then
B b = a;
will not be allowed.
But

B b(a);

will be.
Post by Vladimir Grigoriev
Consider a class
template <typename T>
class Point : public std::pair<T, T>
{
T &x;
T &y;
template <typename U, typename V>
Point( const std::pair<U, V> & );
...
};
and try to write for it three operators
Point + Point;
Point + std::pair;
std::pair + Point
such a way that
std::pair + std::pair
will be impossible.
The point of marking a constructor explicit is so that it cannot be used to perform conversions unintentionally. You have a problem, but you rule out precisely the mechanism designed to solve this very problem. You are painting yourself into a corner.
--
With best wishes,
Igor Tandetnik

With sufficient thrust, pigs fly just fine. However, this is not necessarily a good idea. It is hard to be sure where they are going to land, and it could be dangerous sitting under them as they fly overhead. -- RFC 1925
Duane Hebert
2010-01-26 17:42:17 UTC
Permalink
Post by Vladimir Grigoriev
My example was
A{};
B: public A {};
A a;
B b = a;
If there is a constructor in the B class
explicit B( const A & )
then
B b = a;
will not be allowed.
Ok I see what you are doing.
But I don't like this type of design.
Even if you can write an assignment operator
for B that takes a rhs of A you end up with something that is
confusing. I think that it's normal that if
a = b works, then a == b returns true.
Wouldn't it be better to write a function in B that
takes an A like : B::setAPart(const A &) and
then do
B b;
b.setAPart(a);

I can see that you want to somehow make your point class template
work but that seems odd. Is this a simplification to a real problem?
You don't generally derive a class from another class and then supply
a ctor that takes a base like that. You also don't generally derive from
std classes because they don't have virtual dtors.

Anyway, thanks for the explanation.

Bo Persson
2010-01-25 16:21:59 UTC
Permalink
Post by Vladimir Grigoriev
Ulrich, I forgot to add that in the derived class the constructor
for the base class must be.
B( const A & );
In this case two conversions can be: B ==> A and A ==> B and an
ambiguous reference can occur.
If you can always construct a B from an A, what is the difference? Why
are they different classes in the first place?


Bo Persson
Post by Vladimir Grigoriev
Vladimir Grigoriev
.
Post by Ulrich Eckhardt
Post by Vladimir Grigoriev
However try to provide that a + a will not compute when a + b, b
+ a, and b + b will compute for two classes when one is derived
from another..
b operator+(a const& l, b const& r);
b operator+(b const& l, a const& r);
b operator+(b const& l, b const& r);
b some_b;
a& a_ref = some_b;
a_ref+a_ref; // now what?
struct c: b {...};
c some_c;
b& b_ref = some_c;
b_ref+b_ref; // and here?
Generally, operator+ is something that rather fits for value
types. Public inheritance usually means the type is rather an
entity type, which don't like being treated as a value.
Uli
--
C++ FAQ: http://parashift.com/c++-faq-lite
Sator Laser GmbH
Geschaftsfuhrer: Thorsten Focking, Amtsgericht Hamburg HR B62 932
Vladimir Grigoriev
2010-01-26 11:53:58 UTC
Permalink
Not always but you allow such possibility. A derived class can add new
features to a base class. And the problem is that the base class do not have
new features of the derived class.

Vladimir Grigoriev
Post by Vladimir Grigoriev
Ulrich, I forgot to add that in the derived class the constructor
for the base class must be.
B( const A & );
In this case two conversions can be: B ==> A and A ==> B and an
ambiguous reference can occur.
If you can always construct a B from an A, what is the difference? Why are
they different classes in the first place?
Bo Persson
Post by Vladimir Grigoriev
Vladimir Grigoriev
.
Post by Ulrich Eckhardt
Post by Vladimir Grigoriev
However try to provide that a + a will not compute when a + b, b
+ a, and b + b will compute for two classes when one is derived
from another..
b operator+(a const& l, b const& r);
b operator+(b const& l, a const& r);
b operator+(b const& l, b const& r);
b some_b;
a& a_ref = some_b;
a_ref+a_ref; // now what?
struct c: b {...};
c some_c;
b& b_ref = some_c;
b_ref+b_ref; // and here?
Generally, operator+ is something that rather fits for value
types. Public inheritance usually means the type is rather an
entity type, which don't like being treated as a value.
Uli
--
C++ FAQ: http://parashift.com/c++-faq-lite
Sator Laser GmbH
Geschaftsfuhrer: Thorsten Focking, Amtsgericht Hamburg HR B62 932
Vladimir Grigoriev
2010-01-21 17:34:57 UTC
Permalink
The problem is the same as with std::min and std::min_element. I do not see
any reasonable explanation why these restrictions were adopted. It seems
that this shrinks functionality of C++ without any serious reason.
For example all that I have heard here relative to std::min is some
references to sort algorithms. However I do not see a relation between
std::min and sort algorithms..

Vladimir Grigoriev
Post by Ulrich Eckhardt
Post by Vladimir Grigoriev
There is no a great sense to compare some size_t value with an object of
type Person. This comparison has no sense. It is a general situation when
converting some object of a class to an integral type has sense while
converting an integral type to an object has no sense. For this purpose
the explicit reywoard was introduced. The same is valid for operators.
i.e. sometimes a op b has sense while b op a has no sense.
I'd say whether switching operands still has sense depends on the operator.
For an equality comparison, I expect it to be fully commutative. Similarly
for inequality, while e.g. greater behaves slightly different.
Are you really implying that a==b makes sense when b==a doesn't? I already
voiced my opinion on comparison of a person with an ID: "I wouldn't
overload operator== for that."
Uli
--
C++ FAQ: http://parashift.com/c++-faq-lite
Sator Laser GmbH
Geschaftsfuhrer: Thorsten Focking, Amtsgericht Hamburg HR B62 932
Ulrich Eckhardt
2010-01-22 08:08:26 UTC
Permalink
Post by Vladimir Grigoriev
The problem is the same as with std::min and std::min_element. I do not
see any reasonable explanation why these restrictions were adopted. It
seems that this shrinks functionality of C++ without any serious reason.
What is "this"? Further, you must know that the STL wasn't developed very
far at the point where it was incorporated in the C++ standard. This
explains at least a few areas that could be improved.
Post by Vladimir Grigoriev
For example all that I have heard here relative to std::min is some
references to sort algorithms. However I do not see a relation
between std::min and sort algorithms..
I do, std::min returns the first element that a sequence would have after
std::sort'ing it, simple as that. In that sense, it makes sense for
std::min to require the same semantics of the predicate as std::sort.

BTW: why did you quote all the following text while not even referring to
it? It makes it hard to guess what you mean....
Post by Vladimir Grigoriev
Vladimir Grigoriev
Post by Ulrich Eckhardt
Post by Vladimir Grigoriev
There is no a great sense to compare some size_t value with an object of
type Person. This comparison has no sense. It is a general situation
when converting some object of a class to an integral type has sense
while converting an integral type to an object has no sense. For this
purpose the explicit reywoard was introduced. The same is valid for
operators. i.e. sometimes a op b has sense while b op a has no sense.
I'd say whether switching operands still has sense depends on the operator.
For an equality comparison, I expect it to be fully commutative.
Similarly for inequality, while e.g. greater behaves slightly different.
Are you really implying that a==b makes sense when b==a doesn't? I
already voiced my opinion on comparison of a person with an ID: "I
wouldn't overload operator== for that."
Uli
--
C++ FAQ: http://parashift.com/c++-faq-lite
Sator Laser GmbH
Geschaftsfuhrer: Thorsten Focking, Amtsgericht Hamburg HR B62 932
Uli
--
C++ FAQ: http://parashift.com/c++-faq-lite

Sator Laser GmbH
Geschäftsführer: Thorsten Föcking, Amtsgericht Hamburg HR B62 932
Martin B.
2010-01-22 09:50:07 UTC
Permalink
Vladimir Grigoriev wrote:

(You know, some people consider it good practise to properly quote
messages in a NG.)
Post by Vladimir Grigoriev
There is no a great sense to compare some size_t value with an object of
type Person. This comparison has no sense. It is a general situation when
converting some object of a class to an integral type has sense while
converting an integral type to an object has no sense. For this purpose the
explicit reywoard was introduced. The same is valid for operators. i.e.
sometimes a op b has sense while b op a has no sense.
Well.
If you have a type where (a special_equals b) does not imply (b
special_equals a) then-do-not-define operator== ! This will utterly
confuse the maintainer that has to add something to the code in March
2011 and will highly increase the likelihood of bugs.
If I would be doing code reviews (which I don't) I would never accept
code that messes in this way with op==
(Likewise, I hate the people here who wrote these stupid assignment
operators that do-not-copy-the-whole-object. It's horrible for maintenance!)

!!Principle of least surprise!!

My Opinion,
Martin
Post by Vladimir Grigoriev
Vladimir Grigoriev
Post by Martin B.
Post by Vladimir Grigoriev
Let consider an example.
There is a class named Person. And it has a member size_t personalID and
an equality operator
bool operator ==( const Person &lhs, size_t targetID );
However there is not a Person constructor with one argument equal to
size_t as a personal ID.
So there is the above equality operator and there is no a reverse
equality operator as
bool operator ==( size_t targetID, const Personal &rhs ); // invalid
Could you explain this to me? What is the actual problem?
class Foo { };
bool operator==(Foo const& lhs, size_t rhs) {
return true;
}
bool operator==(size_t lhs, Foo const& rhs) {
return true;
}
void f() {
Foo o = Foo();
size_t i = size_t();
if( o == i) {
// x
}
else if(i == o) {
// y
}
else {
// z
}
}
cheers,
Martin
Vladimir Grigoriev
2010-01-22 12:19:51 UTC
Permalink
I can not agree with your statement. In real world and in mathematics there
are relations which are not symmetrically equivalent. Sometimes an equality
operator means belonging.

Vladimir Grigoriev
Post by Martin B.
(You know, some people consider it good practise to properly quote
messages in a NG.)
Post by Vladimir Grigoriev
There is no a great sense to compare some size_t value with an object of
type Person. This comparison has no sense. It is a general situation when
converting some object of a class to an integral type has sense while
converting an integral type to an object has no sense. For this purpose
the explicit reywoard was introduced. The same is valid for operators.
i.e. sometimes a op b has sense while b op a has no sense.
Well.
If you have a type where (a special_equals b) does not imply (b
special_equals a) then-do-not-define operator== ! This will utterly
confuse the maintainer that has to add something to the code in March 2011
and will highly increase the likelihood of bugs.
If I would be doing code reviews (which I don't) I would never accept code
that messes in this way with op==
(Likewise, I hate the people here who wrote these stupid assignment
operators that do-not-copy-the-whole-object. It's horrible for
maintenance!)
!!Principle of least surprise!!
My Opinion,
Martin
Post by Vladimir Grigoriev
Vladimir Grigoriev
Post by Martin B.
Post by Vladimir Grigoriev
Let consider an example.
There is a class named Person. And it has a member size_t personalID
and an equality operator
bool operator ==( const Person &lhs, size_t targetID );
However there is not a Person constructor with one argument equal to
size_t as a personal ID.
So there is the above equality operator and there is no a reverse
equality operator as
bool operator ==( size_t targetID, const Personal &rhs ); // invalid
Could you explain this to me? What is the actual problem?
class Foo { };
bool operator==(Foo const& lhs, size_t rhs) {
return true;
}
bool operator==(size_t lhs, Foo const& rhs) {
return true;
}
void f() {
Foo o = Foo();
size_t i = size_t();
if( o == i) {
// x
}
else if(i == o) {
// y
}
else {
// z
}
}
cheers,
Martin
Vladimir Grigoriev
2010-01-21 15:35:34 UTC
Permalink
Maybe I have said something other than you are awaiting.
The C++ Standard requires that for the algorithm std::find type T was
EqualityComparable. Not all classes have an equality operator which
satisfies this requirement.

Vladimir Grigoriev
Post by Martin B.
Post by Vladimir Grigoriev
Let consider an example.
There is a class named Person. And it has a member size_t personalID and
an equality operator
bool operator ==( const Person &lhs, size_t targetID );
However there is not a Person constructor with one argument equal to
size_t as a personal ID.
So there is the above equality operator and there is no a reverse
equality operator as
bool operator ==( size_t targetID, const Personal &rhs ); // invalid
Could you explain this to me? What is the actual problem?
class Foo { };
bool operator==(Foo const& lhs, size_t rhs) {
return true;
}
bool operator==(size_t lhs, Foo const& rhs) {
return true;
}
void f() {
Foo o = Foo();
size_t i = size_t();
if( o == i) {
// x
}
else if(i == o) {
// y
}
else {
// z
}
}
cheers,
Martin
Ulrich Eckhardt
2010-01-21 16:30:09 UTC
Permalink
Post by Vladimir Grigoriev
Maybe I have said something other than you are awaiting.
The C++ Standard requires that for the algorithm std::find type T was
EqualityComparable. Not all classes have an equality operator which
satisfies this requirement.
Generally, you can use find_if() to adapt the find algorithm to anything.
The case that that searching for a T you might want to supply a U (with
U!=T) that is equality-comparable to a T is nothing new. The same applies
e.g. to std::map's find or std::set's find. STLport for example includes
some non-standard extensions that allow doing just that, and I believe the
idea is based on a defect report filed against the C++ standard.

Uli
--
C++ FAQ: http://parashift.com/c++-faq-lite

Sator Laser GmbH
Geschäftsführer: Thorsten Föcking, Amtsgericht Hamburg HR B62 932
Duane Hebert
2010-01-21 17:50:22 UTC
Permalink
Post by Vladimir Grigoriev
Let consider an example.
There is a class named Person. And it has a member size_t personalID and an
equality operator
bool operator ==( const Person &lhs, size_t targetID );
Sorry but are you expecting that your Person is equivalent to a size_t?
Post by Vladimir Grigoriev
However there is not a Person constructor with one argument equal to size_t
as a personal ID.
So there is the above equality operator and there is no a reverse equality
operator as
What has the ctor for your class to do with the == operator for your class?
Maybe I'm missing someting...
sasha
2010-01-21 19:28:58 UTC
Permalink
Post by Duane Hebert
What has the ctor for your class to do with the == operator for your class?
Maybe I'm missing someting...
It may need to be constructed before compared - like when OP said during
iteration of an array of IDs.
Duane Hebert
2010-01-21 19:44:14 UTC
Permalink
Post by sasha
Post by Duane Hebert
What has the ctor for your class to do with the == operator for your class?
Maybe I'm missing someting...
It may need to be constructed before compared - like when OP said during
iteration of an array of IDs.
But if I have a ctor taking int, string and another taking string,int how does it relate to the
== operator? This compares instances of an object that has already been constructed.
I can have 27 ctors for a class and only one == operator.
sasha
2010-01-22 00:34:50 UTC
Permalink
Post by Duane Hebert
But if I have a ctor taking int, string and another taking string,int
how does it relate to the
== operator? This compares instances of an object that has already been constructed.
I can have 27 ctors for a class and only one == operator.
The STL algorithms may not use all of available constructors.
Duane Hebert
2010-01-22 13:43:45 UTC
Permalink
Post by sasha
Post by Duane Hebert
But if I have a ctor taking int, string and another taking string,int
how does it relate to the
== operator? This compares instances of an object that has already been constructed.
I can have 27 ctors for a class and only one == operator.
The STL algorithms may not use all of available constructors.
I guess I don't understand what you mean by the STL algorithms using
constructors. Can you give me an example of one that has to construct
something?
sasha
2010-01-22 19:55:19 UTC
Permalink
Post by Duane Hebert
I guess I don't understand what you mean by the STL algorithms using
constructors. Can you give me an example of one that has to construct
something?
What I meant to say of an algorithm needs to iterate over an array of
IDs, but there is no constructor that takes just an ID, then there is a
problem.
Vladimir Grigoriev
2010-01-22 12:43:20 UTC
Permalink
Sorry I have not understood all you have said due to may bad English.

Yes, the class Person has a field for example of size_t which denotes
personal identification. For example this member may play also a role of a
key in a data base. There is no sense to compare two persons because it is
obvious that they are different. However there is a sense to compare an
object of the class Person with a personal identification for example in
search operations or in operations with sets. However there is no a
constructor for Person which takes only one argument - personal ID. Of
course it is artificial example.

Vladimir Grigoriev
.
Post by Duane Hebert
Post by sasha
Post by Duane Hebert
What has the ctor for your class to do with the == operator for your class?
Maybe I'm missing someting...
It may need to be constructed before compared - like when OP said during
iteration of an array of IDs.
But if I have a ctor taking int, string and another taking string,int how
does it relate to the
== operator? This compares instances of an object that has already been constructed.
I can have 27 ctors for a class and only one == operator.
Duane Hebert
2010-01-22 13:41:39 UTC
Permalink
Post by Vladimir Grigoriev
Sorry I have not understood all you have said due to may bad English.
Yes, the class Person has a field for example of size_t which denotes
personal identification. For example this member may play also a role of a
key in a data base. There is no sense to compare two persons because it is
obvious that they are different. However there is a sense to compare an
object of the class Person with a personal identification for example in
search operations or in operations with sets. However there is no a
constructor for Person which takes only one argument - personal ID. Of
course it is artificial example.
What confused me is the use of == to do this. This is typically a comparison
operator that works on the instances. If I see if(a == b) in code I would assume that
it's comparing a and b, not a.i and b.i. If you want to compare two instances based
on a member why not just write a function to do that or a functor that you can
use with std::find ?

Still not sure what the ctor has to do with it. You wouldn't need to construct
anything. You would just need to pass the two instances by reference:
//not tested
struct sameID{
bool operator()( const Person& lhs, const Person& rhs ) const {
return( lhs.id == rhs.id);
}
};

struct LessID{
bool operator()( const Person& lhs, const Person& rhs ) const {
return( lhs.id < rhs.id);
}
};


Then use these as predicates in std algorithms like sort, find etc.


Or maybe I still don't understand your question.
John H.
2010-01-21 22:10:45 UTC
Permalink
Post by Vladimir Grigoriev
Let consider an example.
There is a class named Person. And  it has a member size_t personalID and an
equality operator
bool operator ==( const Person &lhs, size_t targetID );
However there is not a Person constructor with one argument equal to size_t
as a personal ID.
So there is the above equality operator and there is no a reverse equality
operator as
bool operator ==( size_t targetID, const Personal &rhs  );   // invalid
Our attempt to use the algorithm std::find_first_of is prohibited by the
Standard which requires equality comparability.
So main area of classes and objects are overboard of STD algorithms. At the
same time there is not something in the definition of find algorithms that
prevents to use them with classes which have an equality operator which does
not satisfies the requirements
if a == b then must be b == a
I am having a hard time determining what the problem is.

On a practical level, if Person has a member "bool operator==(size_t)
const", there is a version of find_first_of that will use that. I
think most compilers would produce the desired results in this manner.
If your concern is that this version of find_first_of requires that
Person be EqualityComparable with it's symmetry invariant (x == y
implies y == x), I think you don't have to worry. c++98's 25.1.4, c+
+03's 25.1.4, and c++0x's 25.2.7do not make such claims.

Further, even if they did make such claims, you could use the other
version of find_first_of that takes a BinaryPredicate. I don't
believe I have seen anywhere that claimed the BinaryPredicate has a
symmetry invariant. Now if your problem is that you don't want to
write a BinaryPredicate to pass this version of find_first_of... well
I agree it would be a small inconvenience.
You mentioned the equality operator "bool operator ==( const Person
&lhs, size_t targetID )". If you place this in Person as a static
member, then you have your BinaryPredicate right there ("bool
Person::operator==(const Person &lhs, size_t targetID)". It seems
reasonable that this would be a static member of the class rather than
a global member anyways.

Let me know if I am missing your point.
Vladimir Grigoriev
2010-01-22 12:47:02 UTC
Permalink
John you have said exactly what I wanted to say with my bad English!:)

Vladimir Grigoriev
Post by Vladimir Grigoriev
Let consider an example.
There is a class named Person. And it has a member size_t personalID and
an
equality operator
bool operator ==( const Person &lhs, size_t targetID );
However there is not a Person constructor with one argument equal to size_t
as a personal ID.
So there is the above equality operator and there is no a reverse equality
operator as
bool operator ==( size_t targetID, const Personal &rhs ); // invalid
Our attempt to use the algorithm std::find_first_of is prohibited by the
Standard which requires equality comparability.
So main area of classes and objects are overboard of STD algorithms. At the
same time there is not something in the definition of find algorithms that
prevents to use them with classes which have an equality operator which does
not satisfies the requirements
if a == b then must be b == a
I am having a hard time determining what the problem is.

On a practical level, if Person has a member "bool operator==(size_t)
const", there is a version of find_first_of that will use that. I
think most compilers would produce the desired results in this manner.
If your concern is that this version of find_first_of requires that
Person be EqualityComparable with it's symmetry invariant (x == y
implies y == x), I think you don't have to worry. c++98's 25.1.4, c+
+03's 25.1.4, and c++0x's 25.2.7do not make such claims.

Further, even if they did make such claims, you could use the other
version of find_first_of that takes a BinaryPredicate. I don't
believe I have seen anywhere that claimed the BinaryPredicate has a
symmetry invariant. Now if your problem is that you don't want to
write a BinaryPredicate to pass this version of find_first_of... well
I agree it would be a small inconvenience.
You mentioned the equality operator "bool operator ==( const Person
&lhs, size_t targetID )". If you place this in Person as a static
member, then you have your BinaryPredicate right there ("bool
Person::operator==(const Person &lhs, size_t targetID)". It seems
reasonable that this would be a static member of the class rather than
a global member anyways.

Let me know if I am missing your point.
Vladimir Grigoriev
2010-01-22 15:39:16 UTC
Permalink
I wanted to say that for example for the std::find algorithm the requirement
of EqualityComparabality is not correctly or clear formulated because it is
not obligatory that typename std::iterator_traits<InputIterator>::value_type
is the same as T in the algorithm definition.:) In my example with Person an
object of the type Person is converted to size_t and size_t is
EqualityComparable indeed!

Vladimir Grigoriev
Vladimir Grigoriev
2010-01-22 15:58:08 UTC
Permalink
Post by Vladimir Grigoriev
I wanted to say that for example for the std::find algorithm the
requirement of EqualityComparabality is not correctly or clear formulated
because it is not obligatory that typename
std::iterator_traits<InputIterator>::value_type is the same as T in the
algorithm definition.:) In my example with Person an object of the type
Person is converted to size_t and size_t is EqualityComparable indeed!
Vladimir Grigoriev
In other words the expression in the std::find algorithm relative to my
example with the Person

*first == value;

in fact is a real binary predicate! It is a binary predicate that is written
as operator-function inside which after conversion Person to size_t an
equality operator is used.

Vladimir Grigoriev
Vladimir Grigoriev
2010-01-22 16:35:30 UTC
Permalink
So the resume is that the both forms of the algorithm std::find use a
predicate. Only the first form of the std::find uses a special predicate
which is written as an operator-function and as a result of such approach
there is no need to specify it as a parameter in the first form of
std::find.

Vladimir Grigoriev
Continue reading on narkive:
Loading...