Understanting assignments

E

et al.

Hi all! I am following the C++ operator overloading guidelines (*) in
order to create a simple matrix class, with all standard operators
(sum, difference, and so on). Everything worked until I tried to use
the assignment operator! As with the guideline, I started implementing
the compound assignments, then I use them exactly as in the guide to
create an operator (first operator=*, and next using it for defining
operator*).

What I am battling with this compilation error:

error: no match for 'operator=' in 'p = #'obj_type_ref' not supported
by dump_expr#<expression error>(((matrix&)(& o)))'
note: candidates are: virtual matrix& matrix::eek:perator=(matrix&)

within this simple assignment:

matrix p, a, o;
// Do something with "a" and "o", then:
p = a * o; // <==== BAM! Compilation error!

My class "matrix" defines all the operators I need as in the guideline:

class matrix {
// ...
virtual matrix& operator=(matrix& src);

virtual matrix& operator+=(matrix& src);
virtual matrix& operator-=(matrix& src);
virtual matrix& operator*=(double src);
virtual matrix& operator*=(matrix& src);

virtual matrix operator+(matrix& src);
virtual matrix operator-(matrix& src);
virtual matrix operator*(double src);
virtual matrix operator*(matrix& src);

virtual bool operator==(matrix& src);
virtual bool operator!=(matrix& src);
// ...
};

However, if I change a little the assignment using the compound ones,
it works perfectly:


matrix p, a, o;
// Do something with "a" and "o", then:
p = a;
p *= o;
// It works like a charm


I am sorry I can't understand the error, I am still learning! Can you
point me in the right direction?

Thanks & cheers!


(*) URL:
http://www.cs.caltech.edu/courses/cs11/material/cpp/donnie/cpp-ops.html
 
S

SG

Oh, and there's one more thing I overlooked before. The functions
themselves should be declared const, too, because they don't change
their own matrix.

matrix const operator+(matrix const& src) const;
^^^^^
This const is debatable. I never felt the need to return objects by
const value. And with C++0x coming along you're effectivly disabling
move semantics with this.
...
bool operator==(matrix const& src) const;

Also, for symmetry reasons the above two operators could be replaced
by free functions.

Cheers!
SG
 
S

SG

SG ha scritto:


How else do you prevent (a * b) = c?

This is also an item in Effective C++.

Yes. I have that book (3rd edition). I like it. But this is one of the
very few rules I don't agree with. And I'm sure that if a C++0x-aware
edition came out, it would say something different.

If you want this code fragment to be ill-formed, you also have the
option of adding a ref qualifier to your assignment operator in C++0x.

Anyhow, for about 10 years, I'm using programming languages that have
== as comparison operator. I really can't recall the last time (if
ever) I made this mistake you're referring to.
I've not studied C++0x move semantics enough. But surely there's a way
to have both the efficiency gained by move semantics and the prevention
of (a * b) = c.

Yes:

class foo {
...
foo& operator=(foo const&) &;
...
};


Cheers!
SG
 
V

Victor Bazarov

Pete Becker ha scritto:


But what if I don't want others (i.e. users of my class) to write it, or
not to write it by accident?

Accident? Seriously? What, a typo when you intended to write

(a * b) == c

? With matrices? Strongly doubt that. As Pete says, you're spending
time worrying about something that almost never happens.
Since when does adding "const" to a function signature waste time that
could be spent for "real" work?

Any time you do that to the code you inherit (and that doesn't contain
those const qualifiers), you'd not be doing real work, you'd be wasting
everybody's time.

V
 
S

SG

Oh, I see: this is the = instead of == typo. Easily caught by unit
tests. Still way down on my list.

Since one would want to use it in a boolean context, it won't even
compile unless the type in quetion is also convertible to something
"bool-like". I don't think that the matrix class needs to be
convertible to a "bool-like".

Cheers!
SG
 
K

Kai-Uwe Bux

Christian said:
Victor Bazarov ha scritto:


No, I was thinking of typos like when someone intended to write a = (b *
c).

I don't recall an instant of screwing up that way. Maybe, I was just lucky;
but I have a feeling that this kind of typo might be rather rare.
Changing existing files which are used by everyone else on the project
is a different story. I was more thinking of the case when a class is
designed and you are writing new code to be added to the project.

Actually, that would depend on the coding guidelines for the project. If
there is established precedence that operators return by value rather than
by const value, I would go with the precedence. After all, users of my
matrix class may try things like

(B*C).swap( A ); // simulating a move to A

to speed up performance in bottlenecks. The point is that coding guidelines
and precedence shape rational expectations, which one might not want to
break within a project.


Best

Kai-Uwe Bux
 
Ö

Öö Tiib

Pete Becker ha scritto:
Oh, I see: this is the = instead of == typo. Easily caught by unit
tests.

If it's a library used by someone else, then your own unit tests cannot
catch the typo.

I'm quite surprised that such a harmless [*] hint, backed by a
recommendation in of the most recommended C++ books, would cause so much
discussion on the grounds that it's not important enough :)

[*] except of the problem with move semantics, which I had not been
aware of before

Sorry, i do not care really about issue itself, up to designer of
interface and its contract IMO. Just particular detail makes me
wonder... how does it compile? for example:

Matrix a, b, c;
if ( ( a * b ) = c )
{
// blah-blah
}

It should say you cannot convert a Matrix to bool. Or is there some
safe bool? What does it indicate? if ( a ) sounds similarily
nonsensical about Matrix.
 
V

Victor Bazarov

Pete Becker ha scritto:
Oh, I see: this is the = instead of == typo. Easily caught by unit tests.

If it's a library used by someone else, then your own unit tests cannot
catch the typo.


I'm quite surprised that such a harmless [*] hint, backed by a
recommendation in of the most recommended C++ books, would cause so much
discussion on the grounds that it's not important enough :)


[*] except of the problem with move semantics, which I had not been
aware of before

When I encounter code

SomeType const function(arguments...);

then the first thing that springs into my mind is, "it's a typo and the
implementor forgot to add either * or & before the function name..." It
is idiomatic to see

SomeType function(arguments...);

or

SomeType const& function(arguments...);

The recommendations to write anything different require (in my book
anyway) that the reason better be important. Harmless hint? No, it's a
step away from very common idioms, you see.

V
 
V

Victor Bazarov

Pete Becker ha scritto:
Oh, I see: this is the = instead of == typo. Easily caught by unit
tests.

If it's a library used by someone else, then your own unit tests cannot
catch the typo.

I'm quite surprised that such a harmless [*] hint, backed by a
recommendation in of the most recommended C++ books, would cause so much
discussion on the grounds that it's not important enough :)

[*] except of the problem with move semantics, which I had not been
aware of before

Sorry, i do not care really about issue itself, up to designer of
interface and its contract IMO. Just particular detail makes me
wonder... how does it compile? for example:

Matrix a, b, c;
if ( ( a * b ) = c )
{
// blah-blah
}

It should say you cannot convert a Matrix to bool. Or is there some
safe bool? What does it indicate? if ( a ) sounds similarily
nonsensical about Matrix.

Somebody might design the class that calculates some kind of
characteristic of the matrix to be used in shortcuts like those. I am
not advocating it, but don't dismiss it simply because you're not going
to use it. Correctness of the interface is in the eye of the beholder.
Of course, just like with other things, implicit conversions are
dangerous and better avoided, blah blah... All falls into the style
category, as far as I'm concerned.

V
 
Ö

Öö Tiib

Maybe the influence of Meyers' books on my mind has been too strong, but
to me, "SomeType operator+(SomeType const &)" is what looks "strange",
and not the const version. YMMV.


Yeah, matter of taste. For example the one that looks most familiar to
me is:

class SomeType
{
// ...
friend SomeType operator+(const SomeType&, const SomeType&);
// ...
}

That is because i always try to write binary operators (besides =) as
non-member non-friends and if i can't, as friends. why to argue? There
are only very tiny differences.
 
J

James Kanze

[...]
When I encounter code
SomeType const function(arguments...);
then the first thing that springs into my mind is, "it's
a typo and the implementor forgot to add either * or & before
the function name..." It is idiomatic to see
SomeType function(arguments...);

SomeType const& function(arguments...);
The recommendations to write anything different require (in my
book anyway) that the reason better be important. Harmless
hint? No, it's a step away from very common idioms, you see.

I understand this point of view, but a lot depends on who's
doing the recommendation. Scott Meyers is a "recognized
authority"; the fact that he recommends it in "Effective C++"
means that it is something you should have seen, or will see, or
should expect to see. (This isn't argument by authority, but
rather simple recognition of the fact that Scott Meyers' books
have influenced many people, so you will see code following his
recommendations.)

The other issue is coding conventions. Within a given program,
all code should be written according to the same conventions.
If you see
SomeType const function(...);
once, and everywhere else, it's without the const, then the most
obvious explination is that the programmer forgot the &. If
every returned value of class type has a const, then it's almost
certainly the house style, and not an oversight.

FWIW: I don't use the const, despite Scott's recommendation.
I'd gotten into the habit of not using it before reading Scott's
recommendation, and in practice, I don't find writing = for ==
a problem. You do it once or twice the first couple of weeks,
and then you learn. And of course, user defined types don't
normally convert implicitly to bool anyway, unless you're
abusing conversions. So it's not really a problem. (A more
useful requirement would be that operator++ and operator-- are
always a free functions, rather than members, so you can only
call them on lvalues.) But it wouldn't bother me if
I encountered code which did systematically, and if I were to
work for a company where adding the const were house rules, I'd
adapt.
 
A

Alf P. Steinbach /Usenet

* James Kanze, on 26.08.2010 10:38:
[...]
When I encounter code
SomeType const function(arguments...);
then the first thing that springs into my mind is, "it's
a typo and the implementor forgot to add either * or& before
the function name..." It is idiomatic to see
SomeType function(arguments...);

SomeType const& function(arguments...);
The recommendations to write anything different require (in my
book anyway) that the reason better be important. Harmless
hint? No, it's a step away from very common idioms, you see.

I understand this point of view, but a lot depends on who's
doing the recommendation. Scott Meyers is a "recognized
authority"; the fact that he recommends it in "Effective C++"
means that it is something you should have seen, or will see, or
should expect to see. (This isn't argument by authority, but
rather simple recognition of the fact that Scott Meyers' books
have influenced many people, so you will see code following his
recommendations.)

Scott wrote that advice before moving from rvalues was recognized as important.

With Andrei's Mojo effort Andrei declared Scott's earlier advice dead (very
publicly, in DDJ). Simply put, new programming paradigms influence how we best
use old constructs.

Ad I'm 99.95% certain that Scott agrees with that, that that old way is dead
now, considering move semantics with C++0x.


[snip]

Cheers,

- Alf
 
E

et al.

et al. ha scritto:


That's because you don't pass the argument of operator= by const&, so
the temporary, nameless matrix created by "a * o" cannot be assigned to
p.

So, I can then use safely const, but I have a simple problem when
accessing an element.

In one subclass, I re-implemented the at() member: this is for a banded
symmetric matrix, so I store only part of the matrix, leaving the rest
as 0.0. My member is as follows:

double& at(unsigned int r, unsigned int c)

One thing that I couldn't do is returning 0.0 by reference, so I added
a double member "zero" that the constructor sets to 0.0, and if needed
I return that in at(). However, this is contrary to the fact that I
would like zero to be "const"! Do you know how I can avoid this ugly
implementation with a "const" ?


First of all, remove "virtual" from all operators. You usually should
not combine operator overloading with OOP. Besides, you surely won't
derive from matrix, do you?

Well, I am, actually. I am learning through exercises, so I am deriving
"vector" from the "matrix" class, and a "symmatrix" (banded and
symmetric) one. Both subclasses re-implement some methods, but no
operators that I can think of. Why do you recommend that operators
should never be virtual? (Anyway, I removed them)
Again, remove virtual, add const. Additionally, the return value of
these functions should be "matrix const", rather than "matrix".
Otherwise, statements such as the following will not result in a
compiler error (as they should):

(a * o) = p;

matrix const operator+(matrix const& src);
etc.

Allright, I begin to see why this is recommendable!

That's because no temporary, nameless objects are involved.

If you correctly add const to the function signatures, all those
problems and inaccuracies will go away.


Thank you very much! I am now using const references without any pain! :)


Only one question: I am now implementing the const member at() for
accessing an element. First I implemented the "reference" at() that
allows me to modify elements of the matrix:

double& matrix::at(unsigned int r, unsigned int c)

and then, I implemented another const member at(), calling the first one:

double const matrix::at(unsigned int r, unsigned int c) const

Is it safe to do this? I think so, since when in the second I call just
"return at(r, c);", it should create a temporary double. Am I right or
completely wrong?

Thanks & cheers!
 
J

Jorgen Grahn

.
[...] in practice, I don't find writing = for ==
a problem. You do it once or twice the first couple of weeks,
and then you learn.

Perhaps I'm spoiled by g++, but it warns me if I use a = b in a
boolean context. I think I get to see that warning once a year or so
-- but I often write sloppily, relying on compilation and sometimes
unit tests to catch most errors, and then review before checking in to
version control.

/Jorgen
 
J

James Kanze

* James Kanze, on 26.08.2010 10:38:
[...]
I understand this point of view, but a lot depends on who's
doing the recommendation. Scott Meyers is a "recognized
authority"; the fact that he recommends it in "Effective
C++" means that it is something you should have seen, or
will see, or should expect to see. (This isn't argument by
authority, but rather simple recognition of the fact that
Scott Meyers' books have influenced many people, so you will
see code following his recommendations.)
Scott wrote that advice before moving from rvalues was
recognized as important.

Which could easily be yesterday. Most practicing C++
programmers have never heard of moving from rvalues. Precisely
because it isn't important to them. I'd heard about it, because
I followed the evolving standards more than most, but I hadn't
encountered a context where it was important before a year ago.
It very much depends on what you're application is doing, and
for most applications, it's completely irrelevant. (Even for
applications where it's relevant, there are generally acceptable
work-arounds. It's a useful addition to C++, for some
applications, but it's hardly a world shaker.)
With Andrei's Mojo effort Andrei declared Scott's earlier
advice dead (very publicly, in DDJ). Simply put, new
programming paradigms influence how we best use old
constructs.
Ad I'm 99.95% certain that Scott agrees with that, that that
old way is dead now, considering move semantics with C++0x.

Which is neither here nor there. As I said, this is one piece
of Scott's advice I never actually practiced, but others may
have, and that's not a problem either. Perhaps his advice would
be different today---I can understand the argument with regards
to move semantics, even in cases where they aren't currently
important. But the fact remains that many people do follow his
previous practice, and it's not a problem either. (The only
problem is if you stop being consistent.)
 
S

SG

Scott wrote that advice before moving from rvalues was recognized as important.

With Andrei's Mojo effort Andrei declared Scott's earlier advice dead (very
publicly, in DDJ). Simply put, new programming paradigms influence how we best
use old constructs.

Ad I'm 99.95% certain that Scott agrees with that, that that old way is dead
now, considering move semantics with C++0x.

I even have a recent quote on that:

Scott said:
And this SM no longer offers that advice, because returning const
rvalues disables move semantics for the temporaries thus generated.

Scott

[taken from a comp.std.c++ thread: "Modifying rrefs to constants"]

Cheers!
SG
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,967
Messages
2,570,148
Members
46,694
Latest member
LetaCadwal

Latest Threads

Top