insertion operator and const reference

B

bipod.rafique

Hello All,

I need your help in understanding something.

I have a simple class

class test{

};

And I also have a simple insertion operator:

ostream& operator<<(ostream& out, test &a){
out<<"testing";
}

(it is just for testing.)
Note that I do not have const reference to the "test" object in the
insertion operator function.

Now from another function (say main), I have this

int main(){
test a
cout<<a<<endl;

return 0;

}

It works fine, and I get "testing" as output.

But if I have a function say:

test f(){
test a;
return a;
}

[this returns a test object]

and have my main as this:

int main(){

cout<<f()<<endl;

return 0;

}

I get errors!!!, and top errors says I don't have a matching operator<<
in std:cout<<f()()

Now what is that? and why is that?

To fix it, I pass the test object in insertion operator as *const*
reference. It all works. Even if I pass test object by value, its
works!.

So whats the deal? Is there something special when returing an object
from a function?

BTW: I do send objects as const references whenever possible, but why
should it be *necessary* to send as const?

Thanks
Bipod
 
B

bipod.rafique

Just tested another thing. It above problem exists when working it
temporary objects.

So if insertion operator takes the object as reference (and not as
const reference or copy), the following would not work either

int main(){
cout<<test()<<endl;
}

It gives the same error message.

Don't know why though!
 
P

puzzlecracker

Just tested another thing. It above problem exists when working it
temporary objects.

So if insertion operator takes the object as reference (and not as
const reference or copy), the following would not work either

int main(){
cout<<test()<<endl;
}

It gives the same error message.

Don't know why though!


cout<<f(); returns a temprary object whose life is at the stament
level: it will be destroyed at the end of the statement that is.

a. if pass by value, object is copied to the function's paramer value -
that is fine

b. passing by const reference is also fine

c. however, if you pass by reference, in the function you will can
modify unmodifyalbe object - ICEURN!


Easier: you f() returns [temp] const test obeject

f(test t) is fine, since you're not modifying object directly,
f(const t &) is fine, since you promise that object won't be modified.
f(t &) here is make no promises, thus violating const-correctness.


for ref. http://www.parashift.com/c++-faq-lite/const-correctness.html
 
K

Kai-Uwe Bux

Just tested another thing. It above problem exists when working it
temporary objects.

So if insertion operator takes the object as reference (and not as
const reference or copy), the following would not work either

int main(){
cout<<test()<<endl;
}

The slogan is: you cannot bind a temporary to a non-const reference. The
precise rules are in 8.5.3/5 of the standard which is a pain to read. Do
not ask me for a rational: I have no idea why this restriction exists. But
it is easily circumvented:

/*
| lvalue_cast( non-const expression ) creates an lvalue out
| of anything, including a temporary. Just make sure, the
| expression is not constant.
*/

template < typename T >
T & lvalue_cast ( const T & ref ) {
return( const_cast< T & >( ref ) );
}



Another trick that you can try is this:

std::cout<< std::dec << test() << std::endl;

Now, why this works is explained somewhere in the archives of this news
group :)


Best

Kai-Uwe Bux
 
K

Kai-Uwe Bux

puzzlecracker said:
cout<<f(); returns a temprary object whose life is at the stament
level: it will be destroyed at the end of the statement that is.

a. if pass by value, object is copied to the function's paramer value -
that is fine

b. passing by const reference is also fine

c. however, if you pass by reference, in the function you will can
modify unmodifyalbe object - ICEURN!


Nope: temporaries are not unmodifiable. It just so happens that it is
usually pointless to modify them. However, you could do funny stuff using
static members or side effects of the destructor. In any case, temporaries
can be modified:

#include <iostream>

struct xxx {

static int s;

int l;

xxx ( int i )
: l ( i )
{}

~xxx ( void ) {
std::cout << "local: " << l << '\n';
}


void modify ( int i ) {
l = i;
s = i;
}

};

int xxx::s = 0;

int main ( void ) {
xxx( 3 ).modify( 5 );
std::cout << "static: " << xxx::s << '\n';
}


prints:
local: 5
static: 5


[snip]


And, what is the meaning of ICEURN?


Best

Kai-Uwe Bux
 
P

puzzlecracker

Kai-Uwe Bux said:
puzzlecracker said:
cout<<f(); returns a temprary object whose life is at the stament
level: it will be destroyed at the end of the statement that is.

a. if pass by value, object is copied to the function's paramer value -
that is fine

b. passing by const reference is also fine

c. however, if you pass by reference, in the function you will can
modify unmodifyalbe object - ICEURN!


Nope: temporaries are not unmodifiable. It just so happens that it is
usually pointless to modify them. However, you could do funny stuff using
static members or side effects of the destructor. In any case, temporaries
can be modified:

#include <iostream>

struct xxx {

static int s;

int l;

xxx ( int i )
: l ( i )
{}

~xxx ( void ) {
std::cout << "local: " << l << '\n';
}


void modify ( int i ) {
l = i;
s = i;
}

};

int xxx::s = 0;

int main ( void ) {
xxx( 3 ).modify( 5 );
std::cout << "static: " << xxx::s << '\n';
}


prints:
local: 5
static: 5


[snip]


And, what is the meaning of ICEURN?


Best

Kai-Uwe Bux

I don't agree with this trick as a modification of the temporary. All
modification was done at the statement level, which was expectedly
followed by the destruction of the object - that is allowed.
Furthermore, you modified static member, which doesn't belong to the
temporary. You used temporary as a proxy to modify static mem.
variable. Were you to declare that variable as a const temporary,
modify() member function wouldn't have been invoked. Agree?

What is the with std:dec? I had never heard of it?
Mind to elaborate?
 
V

Victor Bazarov

puzzlecracker said:
[...]
I don't agree with this trick as a modification of the temporary.

What is it, then?
> All
modification was done at the statement level, which was expectedly
followed by the destruction of the object - that is allowed.

Uh... So, you concede that modification was, in fact, done?
Furthermore, you modified static member, which doesn't belong to the
temporary.

Kai-Uwe modified both.
> You used temporary as a proxy to modify static mem.
variable.

No, not just as a proxy. Read the example again.
> Were you to declare that variable as a const temporary,
modify() member function wouldn't have been invoked. Agree?

What's a "const temporary" and how do you "declare" one? The syntax used
in the example ("<type-id>()") is not a declaration and it cannot create
a const object. And yes, if the class had two overloaded functions, one
const and the other non-const, a non-const function is preferred even for
a temporary; exactly because the temporary in _that_case_ is non-const.
What is the with std:dec? I had never heard of it?
Mind to elaborate?

(a) It's 'std::dec'
(b) It's called "I/O manipulator". Read up on them.

V
 
K

Kai-Uwe Bux

puzzlecracker said:
Kai-Uwe Bux said:
puzzlecracker wrote: [snip]
c. however, if you pass by reference, in the function you will can
modify unmodifyalbe object - ICEURN!


Nope: temporaries are not unmodifiable. It just so happens that it is
usually pointless to modify them. However, you could do funny stuff using
static members or side effects of the destructor. In any case,
temporaries can be modified:

#include <iostream>

struct xxx {

static int s;

int l;

xxx ( int i )
: l ( i )
{}

~xxx ( void ) {
std::cout << "local: " << l << '\n';
}


void modify ( int i ) {
l = i;
s = i;
}

};

int xxx::s = 0;

int main ( void ) {
xxx( 3 ).modify( 5 );
std::cout << "static: " << xxx::s << '\n';
}


prints:
local: 5
static: 5


[snip]


And, what is the meaning of ICEURN?


Best

Kai-Uwe Bux

I don't agree with this trick as a modification of the temporary. All
modification was done at the statement level, which was expectedly
followed by the destruction of the object - that is allowed.

Well, all modifications of objects are expectedly followed by the
destruction of the object: you cannot modify an object after it died, and
every object dies eventually, unless you call exit.
Furthermore, you modified static member, which doesn't belong to the
temporary. You used temporary as a proxy to modify static mem.
variable. Were you to declare that variable as a const temporary,
modify() member function wouldn't have been invoked.

What is a const temporary? I called a non-const function of an object. To
the compiler that is a possibly modifying operation. The object was a
temporary. Thus, modifying a temporary is legal.

No, the object was modified during its lifetime -- like any other object.
That the lifetime of the object is just one statement is immaterial. I
could make that statement last for the execution of the whole program:

int dummy_main ( void ) {
here goes the real program
}

int main ( void ) {
temporary().modify(), dummy_main();
}

More important, though, is that the modifications of the temporary can have
effects that survive: consider

struct xxx {

some_type* a_pointer;

xxx ( some_type* ptr )
: a_pointer( ptr )
{}

void modify ( ... ) {
do something to the pointee
*a_pointer
}

};

Now, modification of the object has side effects.


Of course, you can always twist language to not say that the temporary is
modified. However, I find it more easy to go with a conventional meaning of
modification: change of a property over time. So here is something more
obvious:

#include <iostream>

struct xxx {

int l;

xxx ( int i )
: l ( i )
{}

xxx& print( void ) {
std::cout << l << '\n';
return( *this );
}

xxx& modify ( int i ) {
l = i;
return( *this );
}

};

int main ( void ) {
xxx( 3 ).print().modify( 5 ).print().modify(1).print();
}


a.out
3
5
1

I would consider it a stretch of language to maintain that the temporary is
*not* modified although the value of its member variable clearly changes
twice.

What is the with std:dec? I had never heard of it? Mind to elaborate?

It's an io-manipulator. The effect is that numbers are printed in base 10,
which is the default anyway: so you won't notice the difference.

However, I have to retract my claim that

std::cout << std::dec << temp() << '\n';

would work. I confused it with something else: piping something to a
temporary stream object. For that, you might want to google "tricky
stringstream-based temporary" in the archive for this group. In that
thread, many people (Siemel Naran in particular) explained kindly to me why
and how that works. You will also find that the technique described in that
thread illustrates the use of modifying temporaries.


Best

Kai-Uwe Bux
 
K

Kai-Uwe Bux

Kai-Uwe Bux said:
(e-mail address removed) wrote: [snip]
Another trick that you can try is this:

std::cout<< std::dec << test() << std::endl;

Oops, I got confused. The std::dec applies to the situation where the stream
is a temporary:


#include <fstream>

int main ( void ) {
std::eek:fstream( "test.001" ) << std::dec << "hello world!" << '\n';
std::eek:fstream( "test.002" ) << "hello world!" << '\n';
}


On my machine, this does:

test.001: hello world!
test.002: 0x8048a01

So the first line prints the string whereas the second prints the pointer to
the string (in a way that is up to the implementation).


Best

Kai-Uwe Bux
 

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
474,164
Messages
2,570,901
Members
47,439
Latest member
elif2sghost

Latest Threads

Top