By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
449,423 Members | 1,294 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 449,423 IT Pros & Developers. It's quick & easy.

Passing reference to variable out of scope

P: n/a
Hello Group,

please consider the following code

#include <vector>
#include <iostream>

#define USE_CONST
#define USE_STRING

class a {
public:
#ifdef USE_CONST
const std::string &moo;
a(const std::string &moo) : moo(moo) { };
#else
std::string &moo;
a(std::string &moo) : moo(moo) { };
#endif

void show() {
std::cerr << "String: '" << moo << "'" << std::endl;
};
};

int main() {
#ifdef USE_STRING
std::string str = std::string("Das ist ein Test!");
a abc = a(str);
abc.show();
#else
a xyz = a(std::string("Das ist ein Test!"));
xyz.show();
#endif

return 0;
}

There are four possible combinations of USE_CONST and USE_STRING:

1 USE_STRING && !USE_CONST =Fine
2 USE_STRING && USE_CONST =Fine
3 !USE_STRING && !USE_CONST =Compile Error
4 !USE_STRING && USE_CONST =Compiles, but behaves weird

So it is clear to me that when using number 3, the compiler throws an
error - since the created string object goes out of scope (and lifespan)
immediately after the constructor of "a" is called.

But what bewilders me a bit is that example #4 compiles cleanly without
any warning - yet there is no string output (it's just empty); probably
because it has already been destroyed when the implicit std::string's
destructor was called.

This seems intriguing. The string is already destroyed, however a may
keep a reference (moo) to it? How is that possible?

Greetings,
Johannes

--
"PS: Ein Realname wäre nett. Ich selbst nutze nur keinen, weil mich die
meisten hier bereits mit Namen kennen." -- Markus Gronotte aka "Makus"
aka "Kosst Amojan" aka "maqqusz" in de.sci.electronics
<45**********************@newsspool3.arcor-online.net>
Nov 19 '07 #1
Share this Question
Share on Google+
7 Replies


P: n/a
Johannes Bauer wrote:
So it is clear to me that when using number 3, the compiler throws an
error - since the created string object goes out of scope (and
lifespan) immediately after the constructor of "a" is called.
No, the compiler throws an error because you are trying to bind a const
reference to a non-const reference. This is completely unrelated to the
lifetime issue.
>
But what bewilders me a bit is that example #4 compiles cleanly
without any warning - yet there is no string output (it's just empty);
probably because it has already been destroyed when the implicit
std::string's destructor was called.
Jep. In your case, the lifetime of the string expression extends up to
the lifetime of the complete expression (the construction of xyz), but
not further.
>
This seems intriguing. The string is already destroyed, however a may
keep a reference (moo) to it? How is that possible?
References do not protect the referenced object from destruction - if an
objects dies before the reference to it vanishes, you end up with a
"dangling reference". In that respect, a reference isn't much better
that a pointer.

Btw. as a certain optimization and a feature to dumbfound beginners and
intermediate users, the following code:

const string &x="Hallo";
cout << x;

is correct.

--
IYesNo yes=YesNoFactory.getFactoryInstance().YES;
yes.getDescription().equals(array[0].toUpperCase());
Nov 19 '07 #2

P: n/a
Marco Manfredini schrieb:
Johannes Bauer wrote:
>So it is clear to me that when using number 3, the compiler throws an
error - since the created string object goes out of scope (and
lifespan) immediately after the constructor of "a" is called.

No, the compiler throws an error because you are trying to bind a const
reference to a non-const reference. This is completely unrelated to the
lifetime issue.
Oh, okay. Coming from C, I'm not used to a compiler being so strict on
the "const" keyword, but I like it.
>This seems intriguing. The string is already destroyed, however a may
keep a reference (moo) to it? How is that possible?

References do not protect the referenced object from destruction - if an
objects dies before the reference to it vanishes, you end up with a
"dangling reference". In that respect, a reference isn't much better
that a pointer.
Okay, that sounds a little bit dangerous - as I thought C++ should
guarantee references are always valid (or at least always not-NULL).
Would at least valgrind detect access to a dangling reference?
Btw. as a certain optimization and a feature to dumbfound beginners and
intermediate users, the following code:

const string &x="Hallo";
cout << x;
Although I don't consider myself to be a greenhorn, this still
dumbfounds me. The assignment operator is overloaded for const char* to
invoke the constructor of std::string, meaning

const string &x = std::string("Hallo");

So - tell me, why doesn't the lifespan of the *actual* string end in
that same line, leaving x to be a dangling reference?

Greetings,
Johannes

--
"PS: Ein Realname wäre nett. Ich selbst nutze nur keinen, weil mich die
meisten hier bereits mit Namen kennen." -- Markus Gronotte aka "Makus"
aka "Kosst Amojan" aka "maqqusz" in de.sci.electronics
<45**********************@newsspool3.arcor-online.net>
Nov 19 '07 #3

P: n/a
Johannes Bauer wrote:
const string &x = std::string("Hallo");

So - tell me, why doesn't the lifespan of the *actual* string end in
that same line, leaving x to be a dangling reference?
There is an explicit rule, that if a temporary is used to initialize a
const reference with *scoped* lifetime (ie. auto & global), then the
temporary remains active for the lifetime of the reference. That is the
compiler treats this (more or less) as:

const string __tmpval = std::string("Hallo");
const string &x = __tmpval;
--
IYesNo yes=YesNoFactory.getFactoryInstance().YES;
yes.getDescription().equals(array[0].toUpperCase());
Nov 19 '07 #4

P: n/a
On Nov 19, 4:13 pm, Johannes Bauer <dfnsonfsdu...@gmx.dewrote:
Marco Manfredini schrieb:
Johannes Bauer wrote:
So it is clear to me that when using number 3, the compiler throws an
error - since the created string object goes out of scope (and
lifespan) immediately after the constructor of "a" is called.
No, the compiler throws an error because you are trying to bind a const
reference to a non-const reference. This is completely unrelated to the
lifetime issue.

Oh, okay. Coming from C, I'm not used to a compiler being so strict on
the "const" keyword, but I like it.
This seems intriguing. The string is already destroyed, however a may
keep a reference (moo) to it? How is that possible?
References do not protect the referenced object from destruction - if an
objects dies before the reference to it vanishes, you end up with a
"dangling reference". In that respect, a reference isn't much better
that a pointer.

Okay, that sounds a little bit dangerous - as I thought C++ should
guarantee references are always valid (or at least always not-NULL).
Would at least valgrind detect access to a dangling reference?
Btw. as a certain optimization and a feature to dumbfound beginners and
intermediate users, the following code:
const string &x="Hallo";
cout << x;

Although I don't consider myself to be a greenhorn, this still
dumbfounds me. The assignment operator is overloaded for const char* to
invoke the constructor of std::string, meaning

const string &x = std::string("Hallo");

So - tell me, why doesn't the lifespan of the *actual* string end in
that same line, leaving x to be a dangling reference?
I'm not entirely sure, but according to Stroustrup: "A temporary
created to hold a reference initializer persists until the end of its
reference's scope" (The C++ Programming Language, Pg 98). This is why
the lifespan doesn't end on the same line.

As for the first case, I'm not so sure, but I'll try to guess. You
were passing a temporary object to another function which was
receiving it in a reference parameter. Because the reference was not
visible in the original scope of the temporary, the temporary object
ceased to exist at the end of the expression.
Nov 19 '07 #5

P: n/a
Marco Manfredini schrieb:
const string __tmpval = std::string("Hallo");
const string &x = __tmpval;
Alright, I get that.

What I'm not getting, however: consider the following:

#include <vector>
#include <iostream>

class a {
public:
const std::string &foo;
a(const std::string &bar) : foo(bar) { };

void show() {
std::cerr << "String: '" << foo << "'" << std::endl;
};
};

int main() {
a xyz = a(std::string("Das ist ein Test!"));
xyz.show();

return 0;
}

Here are four possible ways to declare foo/bar (&foo, foo, &bar, bar).
The problem is: it doesn't matter what the constructor declaration of
"bar" looks like, only the *implementation* knows if the object in
question is copied or referenced (i.e. it depends on the declaration of
"foo").

This means an "outsider" needs to know if a class will copy or reference
the given constructor parameters (i.e. the implementation), which is not
really that nice. It would be neat if that could be seen just by looking
at the prototype.

Greetings,
Johannes

--
"PS: Ein Realname wäre nett. Ich selbst nutze nur keinen, weil mich die
meisten hier bereits mit Namen kennen." -- Markus Gronotte aka "Makus"
aka "Kosst Amojan" aka "maqqusz" in de.sci.electronics
<45**********************@newsspool3.arcor-online.net>
Nov 19 '07 #6

P: n/a
I'd like to clear something up on this point...

On case number 4, the output is ''. As I understand, on my machine,
technically, the compiler implements the reference as a pointer, and
since the object-referred-to is destroyed, that pointer is set to
NULL. So in the end, I have some equivalent of
std::cout << (char*)NULL;
Which prints nothing.

If this is indeed the way this behaves, is this undefined behavior?
(ie, what is expected of a "danging reference"?)
Nov 19 '07 #7

P: n/a
On Nov 19, 1:29 pm, coder <plcoderREMOVET...@gmail.comwrote:
On Nov 19, 4:13 pm, Johannes Bauer <dfnsonfsdu...@gmx.dewrote:
Marco Manfredini schrieb:
Johannes Bauer wrote:
>So it is clear to me that when using number 3, the
>compiler throws an error - since the created string
>object goes out of scope (and lifespan) immediately after
>the constructor of "a" is called.
No, the compiler throws an error because you are trying to
bind a const reference to a non-const reference. This is
completely unrelated to the lifetime issue.
Oh, okay. Coming from C, I'm not used to a compiler being so
strict on the "const" keyword, but I like it.
The same rules apply in C (except, of course, that C doesn't
have references). You can't assign a pointer to const to a
pointer to non-const in C.

Of course, that wasn't the problem in your original code.
>This seems intriguing. The string is already destroyed,
>however a may keep a reference (moo) to it? How is that
>possible?
References do not protect the referenced object from
destruction - if an objects dies before the reference to
it vanishes, you end up with a "dangling reference". In
that respect, a reference isn't much better that a
pointer.
Okay, that sounds a little bit dangerous - as I thought C++
should guarantee references are always valid (or at least
always not-NULL). Would at least valgrind detect access to
a dangling reference?
Probably, at least if you tried to use it.
Btw. as a certain optimization and a feature to dumbfound
beginners and intermediate users, the following code:
const string &x="Hallo";
cout << x;
Although I don't consider myself to be a greenhorn, this
still dumbfounds me. The assignment operator is overloaded
for const char* to invoke the constructor of std::string,
meaning
There's no assignment operator involved in the above. There is
an implicit conversion of the char const[6] to an std::string.
const string &x = std::string("Hallo");
So - tell me, why doesn't the lifespan of the *actual*
string end in that same line, leaving x to be a dangling
reference?
Because the standard says that it doesn't.
I'm not entirely sure, but according to Stroustrup: "A
temporary created to hold a reference initializer persists
until the end of its reference's scope" (The C++ Programming
Language, Pg 98). This is why the lifespan doesn't end on the
same line.
As for the first case, I'm not so sure, but I'll try to guess.
You were passing a temporary object to another function which
was receiving it in a reference parameter. Because the
reference was not visible in the original scope of the
temporary, the temporary object ceased to exist at the end of
the expression.
In the original code, there were two references: the argument to
the constructor, and the reference in the class. The first was
initialized by the temporary; if it had had a longer lifetime,
then the temporary's lifetime would have been extended. The
second was initialized by another reference, and so can have no
influence on any temporary, anywhere.

Another way of looking at it is to say that the extension of
lifetime is not transitive.

I might add, however, that reference members are a special case
anywhere, and have rules of their own when initialized with a
temporary. The simple rule is: don't.

--
James Kanze (GABI Software) email:ja*********@gmail.com
Conseils en informatique oriente objet/
Beratung in objektorientierter Datenverarbeitung
9 place Smard, 78210 St.-Cyr-l'cole, France, +33 (0)1 30 23 00 34

Nov 19 '07 #8

This discussion thread is closed

Replies have been disabled for this discussion.