How to initialize an object reference in C++? - c++

I tried to do
MyClass& x;
x = MyClass(a,b,c);
But C++ won't let me do so because it thinks that x is uninitialized at the beginning.
So I tried to do
MyClass& x = MyClass(a,b,c);
But got error saying invalid initialization of non-const reference of type 'MyClass&' from an rvalue of type 'MyClass'
What's wrong with it? It seems that I simply can't do anything now. How do I get around the initialization issue?

An ordinary reference to non-const must be initialized with an lvalue expression (essentially an expression that refers to a memory location), e.g.
MyClass o{ a, b, c };
MyClass& r = o;
If the reference is to const, or if it is an rvalue reference (denoted by &&), then the initializer can instead be an rvalue expression, such as a temporary produced by a function invocation:
MyClass const& rc = foo();
MyClass&& rr = foo();
In these cases, for a local reference the lifetime of the temporary is extended to the scope of the reference.
And one special feature is that if the initializer produces a temporary of a derived class, it's that full derived class object whose lifetime is extended, i.e. there's no slicing to the class specified for the reference.
More generally the reference can be bound to any part of a temporary, as long as that part has a compatible type, and this will extend the lifetime of the full temporary object.

A reference must refer to an already-existing object. So you need to have an object first before you can refer to it.
MyClass y = MyClass(a,b,c);
MyClass &x = y;

Related

Is capturing a newly constructed object by const ref undefined behavior

Is the following (contrived example) okay or is it undefined behavior:
// undefined behavior?
const auto& c = SomeClass{};
// use c in code later
const auto& v = c.GetSomeVariable();
It is safe. Const ref prolongs the lifetime of temporary. The scope will be the scope of const ref.
The lifetime of a temporary object may be extended by binding to a
const lvalue reference or to an rvalue reference (since C++11), see
reference initialization for details.
Whenever a reference is bound to a temporary or to a subobject
thereof, the lifetime of the temporary is extended to match the
lifetime of the reference, with the following exceptions:
a temporary bound to a return value of a function in a return statement is not extended: it is destroyed immediately at the end of
the return expression. Such function always returns a dangling
reference.
a temporary bound to a reference member in a constructor initializer list persists only until the constructor exits, not as
long as the object exists. (note: such initialization is ill-formed as
of DR 1696).
a temporary bound to a reference parameter in a function call exists until the end of the full expression containing that function
call: if the function returns a reference, which outlives the full
expression, it becomes a dangling reference.
a temporary bound to a reference in the initializer used in a new-expression exists until the end of the full expression containing
that new-expression, not as long as the initialized object. If the
initialized object outlives the full expression, its reference member
becomes a dangling reference.
a temporary bound to a reference in a reference element of an aggregate initialized using direct-initialization syntax (parentheses)
as opposed to list-initialization syntax (braces) exists until the end
of the full expression containing the initializer.
struct A {
int&& r;
};
A a1{7}; // OK, lifetime is extended
A a2(7); // well-formed, but dangling reference
In general, the lifetime of a temporary cannot be further extended by "passing it
on": a second reference, initialized from the reference to which the
temporary was bound, does not affect its lifetime.
as #Konrad Rudolph pointed out (and see the last paragraph of above):
"If c.GetSomeVariable() returns a reference to a local object or a reference that it is itself extending some object’s lifetime, lifetime extension does not kick in"
There should be no issue here, thanks to lifetime extension. The newly constructed object will survive until the reference goes out of scope.
Yes this is perfectly safe: the binding to a const reference extends the lifetime of the temporary to the scope of that reference.
Note that the behaviour is not transitive though. For example, with
const auto& cc = []{
const auto& c = SomeClass{};
return c;
}();
cc dangles.
This is safe.
[class.temporary]/5: There are three contexts in which temporaries are destroyed at a different point than the end of the full-expression. [..]
[class.temporary]/6: The third context is when a reference is bound to a temporary object. The temporary object to which the reference is bound or the temporary object that is the complete object of a subobject to which the reference is bound persists for the lifetime of the reference if the glvalue to which the reference is bound was obtained through one of the following: [lots of things here]
It is safe in this specific case. Note however that not all temporaries are safe to capture by const reference... for example
#include <stdio.h>
struct Foo {
int member;
Foo() : member(0) {
printf("Constructor\n");
}
~Foo() {
printf("Destructor\n");
}
const Foo& method() const {
return *this;
}
};
int main() {
{
const Foo& x = Foo{}; // safe
printf("here!\n");
}
{
const int& y = Foo{}.member; // safe too (special rule for this)
printf("here (2)!\n");
}
{
const Foo& z = Foo{}.method(); // NOT safe
printf("here (3)!\n");
}
return 0;
}
The reference obtained for z is NOT safe to use because the temporary instance will be destroyed at the end of full expression, before reaching the printf statement. Output is:
Constructor
here!
Destructor
Constructor
here (2)!
Destructor
Constructor
Destructor
here (3)!

why can temporary objects be bound to const reference?

Source of question:
The only failing case is passing parameters by non-const reference,
since temporary variable couldn't be bound to it.
void DrawLine(const Vector& v1, const Vector& v2);
If the object is temporary, why would making the reference const have any effect on the lifetime of the temporary object?
I guess I also don't fully understand the scope of existence for temporary objects created in an argument.
If the object is temporary, why would making the reference const have any effect on the lifetime of the temporary object?
In the present context, the issue is not the lifetime of the object but whether you can modify it.
Say you make a call.
foo(10);
The object that holds the value 10 in the call should not be modified by the function. If the interface of foo is:
void foo(int& ref);
it's fair to implement foo as:
void foo(int& ref)
{
ref = 20;
}
That would be a problem given the call foo(10). It won't be a problem if foo uses a const&.
void foo(int const& ref)
{
ref = 20; // Not allowed.
}
From C++11 Standard, Temporary Objects/1
Temporaries of class type are created in various contexts: binding a reference to a prvalue ([dcl.init.ref]), returning a prvalue ([stmt.return]), a conversion that creates a prvalue, ...
and from C++11 Standard, References/5.2:
-- Otherwise, the reference shall be an lvalue reference to a non-volatile const type (i.e., cv1 shall be const), or the reference shall be an rvalue reference.
A temporary can only bind to a reference to a prvalue. The type of such a reference must be a const qualified lvalue reference or a rvalue references.
MS Visual Studio compilers have allowed binding of non-const references to temporary objects but it is not sanctioned by the standard.

Is the lifetime of a C++ temporary object created in ?: expression extended by binding it to a local const reference?

It is not clear to me whether the lifetime of a temporary object would be extended by binding it to a const reference in a ?: expression:
class Foo {...};
Foo *someLValue = ...;
const Foo& = someLValue ? *someLValue : Foo();
Is the lifetime of the temporary created by calling the default constructor Foo() extended by binding it to the local const ref even though the binding is conditional? Or does this create a dangling reference because the temporary value of Foo() would be destroyed at the end of the ?: expression?
In this code, the second and third operand of the conditional operator have different value categories (lvalue and prvalue).
That means that the result of the conditional operator is a prvalue of type Foo, which denotes a temporary object copy-initialized from the selected operand.
The reference binds directly to this temporary object and so the temporary's lifetime is extended.
Notes:
The reference never binds directly to *someLValue, nor even to Foo().
The temporary being initialized from Foo() is a copy elision context so you may not be able to observe the temporary in this case.
The temporary is non-const even though the reference is to const.

Will the lifetime of a temporary created in the argument list to a function taking a *non*-const reference parameter encompass the function call?

I have become accustomed to the fact that a const reference extends the lifetime of a temporary until the reference goes out of scope:
class X {};
{
X const & x = X();
// Lifetime of x extended to the end of the scope
// in which x is declared because it was declared
// as a *const* reference
}
... And I am also aware that a temporary lives until the end of the expression in which it is created:
// Contrived example to make a point about lifetime of a temporary in an expression
class Y
{
public:
Y() : y(5) {}
Y & operator+=(int const & rhs)
{
y += rhs;
return *this;
}
int foo() { return y; }
private:
int y;
};
// n in the following line of code is 11 and the code is valid
// - the lifetime of the temporary persists to the end of the expression
int n = (Y() += 6).foo();
Assuming I am correct about both of the above, I suspect that it is true that a temporary created in a function argument list will persist for the lifetime of the function call even if it is bound to a non-const reference:
class X {};
void foo(X & x)
{
// x is valid in this function,
// even though the parameter is declared
// as a *non*-const reference - correct?
}
// Valid, I think, even though the function parameter
// is declared as a **non**-const reference
// - because the lifetime of the temporary persists until the expression
// is fully evaluated - right?
foo(X());
I imagine that my experience and understanding is correct - that it is safe to bind the temporary created in the function argument list to the non-const reference parameter.
But I'd like to confirm I am right about this, because I was not able to find this question explicity answered anywhere.
Thanks!
Sure you are right.
The standard:
12.2 Temporary objects [class.temporary]
[...]
There are two contexts in which temporaries are destroyed at a different point than the end of the fullexpression.
The first context is when a default constructor is called to initialize an element of an array. If
the constructor has one or more default arguments, the destruction of every temporary created in a default
argument is sequenced before the construction of the next array element, if any.
The second context is when a reference is bound to a temporary. The temporary to which the reference is bound or the temporary that is the complete object of a subobject to which the reference is bound persists for the lifetime of the reference except:
— A temporary bound to a reference member in a constructor’s ctor-initializer (12.6.2) persists until the constructor exits.
— A temporary bound to a reference parameter in a function call (5.2.2) persists until the completion of the full-expression containing the call.
— The lifetime of a temporary bound to the returned value in a function return statement (6.6.3) is not extended; the temporary is destroyed at the end of the full-expression in the return statement.
— A temporary bound to a reference in a new-initializer (5.3.4) persists until the completion of the
full-expression containing the new-initializer.
Want to ask the question again using one more level of constructors, and objects storing references?
Deduplicator has given the language lawyer answer, here is the implementation detail answer:
If you pass any object by reference, you are effectively passing a pointer to that object. Since the caller is only passing a pointer to the object, the object that is passed by reference must be constructed by the caller.
The callee, on the other hand, only ever sees the pointer that is passed in. It does not see how the caller has constructed the object whose pointer is passed. This object could be a temporary (only in the case of a const reference, because temporaries cannot be passed as non-const references), or a variable with function scope, it could be an object that was already passed to the caller by its caller, it could even be an object allocated with new. As such, it cannot destruct the object because it does not know how.
Consequently, it is up to the caller to clean up the temporary after the callee returns control to the caller - anything that is passed in by reference must live for the entire runtime of the callee.
Note that this entire argument is completely ignorant as to whether the reference is const or not.

Reference Member Required to be Const?

In this simple example, why do I need to make 'member' const in order to get this to compile?
struct ClassA
{
ClassA(int integer) {}
};
struct ClassB
{
ClassB(int integer):
member(integer)
{
}
const ClassA& member;
};
int main()
{
ClassB* b = new ClassB(12);
return 0;
}
Otherwise, I get this error:
error: invalid initialization of
reference of type 'ClassA&' from
expression of type 'int'
The reason why is that what's actually happening here is you're using an implicit conversion from int to ClassA in the initialization of member. In expanded form it is actually doing the following
member(ClassA(integer))
This means that the ClassA instance is a temporary. It's not legal to have a reference to a temporary variable only a const reference hence you get the compiler error.
The easiest fix is to remove the & modifier on member and make it just of type ClassA
ClassA member;
Additionally it's a good practice to put explicit in front of the ClassA constructor to avoid the silent implicit conversion.
explicit ClassA(int integer){}
Because you are trying to store a reference to a temporary object, and you may only store constant references to temporaries.
When you try to initialize the member reference of type ClassA& with the integer parameter of type int, the implicit conversion constructor ClassA::ClassA(int integer) is inovked to produce an unnamed temporary object of type ClassA from the integer variable. That unnamed temporary object is then used to initialize the reference member, creating a reference-to-temporary, which must be const.
I question your design here. If you're trying to initialize member with data passed by value to the ClassB constructor, having member be a reference is probably not the right thing to do. Why do you think member ought to be a reference and not just an object?
ClassA is a reference member of ClassB, so it must be instantiated with an instance of ClassA.
You're create a temporary, and initializing the reference from that temporary. Just like usual, to bind to a temporary, a reference has to be const.
I'd have serious second thoughts about this. A class with a reference member is rarely useful. When it is useful, you generally want to start with some existing object and pass a reference to that object through to the member reference. When you do this, you need to be sure the referenced object exists for the entire lifetime of the object containing a reference, so the two are quite tightly coupled -- which you'd usually rather avoid.