Click to See Complete Forum and Search --> : const references? I'm confused.
matthias_k
May 9th, 2004, 02:22 PM
Hey again,
I've just read something very confusing in Stroustrups book about so called const references after running into this "problem":
Consider a class C like this:
class C
{
std::string str;
public:
C() { str = "original"; }
const std::string & GetStr() const { return str; }
};
and a program, maybe like this:
C c;
std::string & ref = c.GetStr();
ref = "changed!";
cout << c.GetStr() << endl;
The output is "original". Frankly I would have expected that ref would change the value of C's data member but it doesn't. And it's not because str is const, because it isn't!
The passage about const references in Stroustrup's is somewhat vague... From what I've understood, a reference to a temporary object is returned instead of a reference to the data member itself, therefore ref = "changed" will not change the member but the temp var. However, when I leave the method's scope that temp var would be removed from the stack, so how can I still have a valid reference to it?!
Can someone explain this problem a little more in detail?
matthias_k
May 9th, 2004, 02:27 PM
For further confusion, consider this:
const int & ref = 1;
No that's not a typo, that's valid C++ code. But very strange indeed! :D
wien
May 9th, 2004, 02:59 PM
Did that first example compile at all? It shouldn't, and doesn't on my compiler. You're assigning a const reference to a non-const reference, and that's illegal.
EDIT: typo.
Guysl
May 9th, 2004, 02:59 PM
I'll try to make things clear about reference-
your first code cannot be compiled. you can't return a const
reference to a non const reference, so:
const std::string & GetStr()
cannot return a type to other than const string& var.
std::string & ref = c.GetStr() -----> ref is not const!!
indeed, in your code, when str is been returned,
a temporary const object is returned, but you shouldn't care
about it, since you, the programmer, declared that GetStr()
will return a value that can't be change (const, remember? ) -
so its not your business if the compiler use a temporary object or not.
about the second post:
const int & ref = 1;
yeah it seems at the first time a little weird, but thats how it is.
the only case that a const reference variable can be initialized -
its when it been declared.
Regards,
Guy
matthias_k
May 10th, 2004, 12:22 AM
To both of you:
It compiles with g++ with -Wall, -pedantic and -ansi set.
matthias_k
May 10th, 2004, 09:08 AM
Well, actually it produces a warning, but still compiles. For reference, I have posted my demo code as well as my makefile code with the compiler call:
source:
class C
{
int a;
public:
const int & Get() const { return a; };
};
int main()
{
C c;
int & ref = c.Get();
ref = 5;
return 0;
}
Makefile:
## makro definitions
CC = g++
DEBUG = -g
#CFLAGS = -Wall -pedantic -ansi -c $(DEBUG)
LFLAGS = -Wall -pedantic -ansi $(DEBUG)
LIBS =
OBJECTS = reftest.cpp
EXE = reftest
## makefile targets
$(EXE): $(OBJECTS)
$(CC) $(LIBS) $(LFLAGS) $(OBJECTS) -o $@
all: start $(EXE) finish
clean:
@rm -f $(EXE)
@rm -f *.o
@rm -f *~
@echo "Clean sweep!"
start:
@echo ""
@echo "Compiling $(EXE)..."
@echo ""
finish:
@echo ""
@echo "...success!"
Output:
Compiling reftest...
g++ -Wall -pedantic -ansi -g reftest.cpp -o reftest
reftest.cpp: In function `int main()':
reftest.cpp:11: warning: conversion from `const int' to `int &' discards qualifiers
...success!
Compilation finished at Mon May 10 16:01:58
No error, just a warning. Okay, now here is what happens if I change ref to be a const reference (!):
Compiling reftest...
g++ -Wall -pedantic -ansi -g reftest.cpp -o reftest
reftest.cpp: In function `int main()':
reftest.cpp:12: assignment of read-only reference `ref'
make: *** [reftest] Error 1
Compilation exited abnormally with code 2 at Mon May 10 16:06:07
So much about code only compiling when changing ref to const int & ^^
wien
May 10th, 2004, 09:11 AM
Looks like your compiler is having a bad day to me. What version are you using?
matthias_k
May 10th, 2004, 09:16 AM
Originally posted by wien
Looks like your compiler is having a bad day to me. What version are you using?
2.95.4
TheCPUWizard
May 10th, 2004, 09:17 AM
reftest.cpp:11: warning: conversion from `const int' to `int &' discards qualifiers
This is a SERIOUS warning. Just because something is a warning and not an error does not mean it is ok to leave it.
MOST professional shops prohibit the "checking-in" of any production code that contains warnings.
Once I am past the very early stages of implementation I typically set my build environment to treat ALL warnings as errors.
matthias_k
May 10th, 2004, 09:20 AM
Originally posted by TheCPUWizard
This is a SERIOUS warning. Just because something is a warning and not an error does not mean it is ok to leave it.
MOST professional shops prohibit the "checking-in" of any production code that contains warnings.
Once I am past the very early stages of implementation I typically set my build environment to treat ALL warnings as errors.
True, but that's not the point. The point is that changing ref to be of type const int & produces an error, which is sort of NOT logical to me.
Philip Nicoletti
May 10th, 2004, 09:21 AM
reftest.cpp:12: assignment of read-only reference `ref'
this is a correct error message. The offending line :
ref = 5;
you are trying to change a CONST reference.
you will get the same error with :
const int x = 1;
x = 5;
wien
May 10th, 2004, 09:23 AM
Bleh... Sorry. There is nothing wrong with what your compiler is telling you. The reason the second example fails is the line ref=5. You are assigning a value to a const variable, and that's not allowed. Remove that line and you'll be fine.
matthias_k
May 10th, 2004, 09:29 AM
Oh. Right. I somewhat looked in the wrong place I guess :D Okay....
Great, so we can get back to my original question:
If I hand out a const reference with a getter method, and if that const reference is actually a reference to a copy of the original object, how can I still be able to have a reference to it when I leave the scope of the method (consider that the temp object is removed from the stack once I leave the scope of the getter)?
edit: At least that's what Stroustrup says in his C++ reference in fact that creating a const reference to an object will create a copy of that object and the *copy* will be referenced. That way you cant change the original object, which "simulates" a constant value (but actually isnt).
matthias_k
May 10th, 2004, 09:44 AM
Maybe there's an internal reference counter which keeps track of objects which are still referenced and keeps them alive as long as there are references to it? I don't believe there's a mechanism like this in C++... So why does it work (remember, the first example produced a warning but I could use the reference across the whole program)?
wien
May 10th, 2004, 09:50 AM
I think the reason it "works" in your case, is by the magic of "undefined behaviour". You can't use an object beyond it's scope. (Which is in fact what you're trying to do.) Simply create a copy of it if you need to do that.
matthias_k
May 10th, 2004, 09:58 AM
:(
Well, not really the answer I was hoping for but okay :D Somewhat inefficient to make copies of large objects just to prevent the client from messing with class internals.
Seriously, there must be another way. What do I have all those const modifiers for?!
wien
May 10th, 2004, 10:10 AM
There shouldn't be any problems initializing a const& with the const& returned from the get call if that's what you're asking. (A long as you don't keep it longer than the lifetime of the variable it references that is...)class C
{
int a;
public:
const int & Get() const { return a; };
};
int main()
{
C c;
const int & ref = c.Get();
std::cout << ref;
return 0;
}This works just fine...
matthias_k
May 10th, 2004, 12:58 PM
This works just fine...
And that's what I don't understand. Because:
Get() returns a reference to a temporary copy of C::a instead of C::a itself. Outside Get() that temporary object shouldn't exist any longer and ref would reference an object which is no more.
I'm still missing an explanation for this :)
wien
May 10th, 2004, 01:13 PM
Not having the almighty standard available, I can't say for sure, but are you absolutely positive that is the case? I can't really see why it should be that way.
Consider this example:#include <iostream>
class some_class
{
public:
some_class()
:
a(0)
{}
private:
int a;
public:
const int& get_data() const
{
return a;
}
void alter()
{
a = 100;
}
};
int main()
{
some_class object;
const int & ref = object.get_data();
object.alter();
std::cout << ref;
system("pause");
return 0;
}If ref actually referenced a temporary instead of a like you say, this code should print "0". On my compiler it prints "100", which I would persume to be correct.
matthias_k
May 10th, 2004, 01:57 PM
This is what I've read in the book, maybe I misunderstood that part:
Consider:
double & dr = 1; // error: l-value needed
const double & cdr = 1; // ok
The Interpretation of this last initialization might be:
double temp = double(1);
const double & cdr = temp;
A temporary created to hold a reference initializer persists until the end of its reference's scope.
References to variables and references to constants are distinguished because the introduction of a temporary in the case of the variable is highly error-prone; an assignment to the variable would become an assignment to the -- soon to disappear -- temporary. No such problem exists for references to constants and references to constants are often important as function arguments. [...]
I'm not sure if in the case of returning a const & too will be created a temporary.
wien
May 10th, 2004, 02:42 PM
Okay... Now I get what you're getting at. :)
If I interpret this correctly, the "reference to a temporary" thing only happens if you assign a constant value to a const reference like that first example you posted. (const double & cdr = 1) That is not what you are doing in your class. You are returning a const& to an actual variable, and therefore no temporaries are needed.
If you on the other hand return the constant value 100 in your class instead of 'a', a temporary would be created and kept alive until all references to it have gone out of scope.
matthias_k
May 11th, 2004, 12:32 AM
Yes that makes sense. Alright now I know where I'm at, thanks.
treuss
May 11th, 2004, 07:11 AM
Time to upgrade your compiler. Current g++ versions treat the error in your code as an error, not as a warning.
[treuss]src>g++ -o constref constref.cc
constref.cc: In function `int main()':
constref.cc:11: error: invalid initialization of reference of type 'int&' from
expression of type 'const int'
[treuss]src>g++ --version
g++ (GCC) 3.3.3 (Debian 20040401)
Copyright (C) 2003 Free Software Foundation, Inc.
This is free software; see the source for copying conditions. There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
codeguru.com
Copyright Internet.com Inc., All Rights Reserved.