Reputation: 571
string& message(){
static string str = "good good";
return str
}
In the above code, the returned type of the function should be a reference to a string variable based on its signature. Well, on the other hand, inside of the function definition, a variable of type string is returned. Based on my understanding, object and the reference of object are sort of inter-changable? Or did I miss something here?
Upvotes: 1
Views: 151
Reputation: 156
str
is classified as lvalue reference
in the scope of message()
function. when you return by reference, you return the lvalue reference you already own, str. But when you return an lvalue by value, the copy constructor of that type is invoked, and a new string is constructed (as the instance of the string is static, rvo cannot be performed)
Hence, it boils down to this;
Bear in mind that when you return by reference, you have to guarantee the lifetime of referenced instance outlives the caller site. In your example, you have provided that guarantee by making that variable static
.
Example:
#include <iostream>
struct foo{
foo(){
std::cout << "foo is being default constructed" << std::endl;
}
foo(foo & f){
std::cout << "foo is being copy constructed" << std::endl;
}
};
foo doFoo(){
std::cout << __PRETTY_FUNCTION__ << std::endl;
static foo f;
return f;
}
foo & doFooRef(){
std::cout << __PRETTY_FUNCTION__ << std::endl;
static foo f;
return f;
}
int main(){
auto f1 = doFoo();
auto f2 = doFoo();
// to preserve value type of doFooRef completely. plain auto basically perform std::decay_t over
// doFooRef's return type
decltype(auto) f3 = doFooRef();
decltype(auto) f4 = doFooRef();
}
The output of the code above is as follows (with no optimizations);
foo doFoo()
foo is being default constructed
foo is being copy constructed
foo doFoo()
foo is being copy constructed
foo& doFooRef()
foo is being default constructed
foo& doFooRef()
Let's break it out;
foo doFoo() // the first invocation of doFoo
foo is being default constructed // first invocation triggers static variable initialization inside of doFoo, so default constructor is called
foo is being copy constructed // doFoo returning by value, copy constructor is called
foo doFoo() // the second invocation of doFoo
// note that default construction does not appear again as static variable was initialized in first invocation already
foo is being copy constructed // doFoo returning by value, copy constructor is called
foo& doFooRef() // first invocation of doFooRef
foo is being default constructed // first invocation triggers static variable initialization inside of doFooRef, so default constructor is called
// no copy constructor invocation, because we are returning the reference directly
foo& doFooRef() // second invocation of doFooRef, no default construction, no copy construction as expected.
Upvotes: 0
Reputation: 958
@kkxx
My answer would probably be in a very layman language in the way I understand C++ references.
A reference is basically another name to the variable it is referencing to. It is essentially renaming the variable while the existing name is also accessible (inside the scope). This is the reason why references cannot be left uninitialized as you cannot rename something non existing.
(I am still confused about whether or not we can view the memory address of a reference but that is a topic for another thread(or maybe already existing thread))
So what you are doing is simply returning the reference to the str
variable. it is like you are returning the str
variable but the recipient of the function will be just another name to str
.
Upvotes: 1
Reputation: 4079
They're not interchangeable.
When you pass something by value, you're passing a copy of the entire thing, the recipient can delete it or mutate it or do whatever and the original is left alone.
When you pass a reference, under the hood you're really passing a pointer (address) to the original. If the recipient messes with it, the original is affected. If the original is destroyed and you try to use the reference, you'll get problems.
You can also pass something by pointer explicitly: string* bar = &foo;
.
and by rvalue reference: string bar = std::move(foo);
, which tells the recipient you want to move the data without copying, leaving foo empty.
Upvotes: 1
Reputation: 409176
If you have e.g.
std::string foo = "foo";
std::string& bar = foo;
you basically do the same thing: You use the object foo
to create the reference bar
. After the initialization of bar
, that variable is a reference to foo
.
The same thing happens in your function: When you do return str;
the compiler will create a reference to str
and return that reference.
Upvotes: 3