Initializer list and const& - is this legal C++ [duplicate] - c++

This question already has answers here:
Does a const reference class member prolong the life of a temporary?
(6 answers)
Closed 6 years ago.
Consider:
#include <iostream>
#include <vector>
class A
{
public:
const int& i;
};
class B
{
public:
const std::vector<int>& i;
};
int main()
{
A a = { 3 };
std::cout << a.i << std::endl;
B b = { { 1, 2 } };
std::cout << b.i[0] << " " << b.i[1] << std::endl;
}
On VS2015 update 3, this crashes in runtime on the last line because the vector b.i is empty; on gcc (4.9.2) this runs OK and shows the expected output (3 1 2). So on VS it 'works' (does what I expected) for an int, but not a vector.
Is this a VS bug or is it just an accident that it works on gcc?

The first is OK, temporary's lifetime is extended when assigned to a reference. Second is UB AFAIK, because { { 1, 2 } } is a std::initializer_list<>, not a std::vector<> directly. Prolonging lifetime temporary objects is not transitive (i.e., it lasts till the end of current function, in this case, the constructor), only local ones get prolonged.

It works on gcc by accident, because this is definitely undefined behavior.
In order to satisfy B's initialization, the compiler needs to construct a temporary vector<int>. Since the reference to that vector is const, the compiler does not see a problem with using it to initialize B. However, the temporary object becomes invalid as soon as the initialization is over, so accessing it by reference outside of initialization is undefined behavior.

Related

Why initialization in this way has something to do with the copy constructor? [duplicate]

This question already has answers here:
Why C++ copy constructor must use const object?
(8 answers)
Why is the copy-constructor argument const?
(8 answers)
Why do we use const and reference for arguments to copy constructors?
(1 answer)
How come a non-const reference cannot bind to a temporary object?
(11 answers)
Closed 7 months ago.
I am a newcomer to learning C++. I just tried to run the following code in vs code, but it reported an error
#include <iostream>
using namespace std;
class Entity
{
public:
Entity()
{
cout << "default" << endl;
}
Entity(int x)
{
cout << x << endl;
}
Entity(Entity &e)
{
cout << "copy" << endl;
}
Entity &operator=(const Entity &e)
{
cout << "assignment" << endl;
return *this;
}
};
int main(void)
{
Entity e = Entity(8);
}
The error is cannot bind non-const lvalue reference of type 'Entity&' to an rvalue of type 'Entity' Entity e = Entity(8);
I am searching for a long time on net. Some people say this is because compiler created temporary objects cannot be bound to non-const references, so adding const modifier will work.
Entity(const Entity &e)
{
cout << "copy" << endl;
}
It seems that I am initializing an object with the copy constructor, otherwise the compiler should not report an error. But the fact is I'm using the parameterized constructor to do that.
What puzzles me is why initialization in this way has something to do with the copy constructor?
Entity(8); calls the constructor Entity(int x). Then you use that temporary to construct e. Because it is a temporary it cannot be passed to a method that has a non-const reference argument.
For further details I refer you to What are copy elision and return value optimization?. In short: The copy constructor must be available but the copy is then elided. Thanks to #MilesBudnek for the link.

Is there a way to be sure that a const reference stored in class will always refer to a valid object?

I have written the following sample code:
#include <iostream>
class B
{
int Value;
public:
B(int V) : Value(V) {}
int GetValue(void) const { return Value;}
};
class A
{
const B& b;
public:
A(const B &ObjectB) : b(ObjectB) {}
int GetValue(void) { return b.GetValue();}
};
B b(5);
A a1(B(5));
A a2(b);
A a3(B(3));
int main(void)
{
std::cout << a1.GetValue() << std::endl;
std::cout << a2.GetValue() << std::endl;
std::cout << a3.GetValue() << std::endl;
return 0;
}
Compiled with mingw-g++ and executed on Windows 7, I get
6829289
5
1875385008
So, what I get from the output is that the two anonymous object are destroyed as the initialization has completed, even if they are declared in a global context.
From this my question: does is exist a way to be sure that a const reference stored in class will always refer to a valid object?
One thing you can do in class A:
A(B&&) = delete;
That way, the two lines that try to construct an A from a B temporary will fail to compile.
That obviously won't prevent you providing some other B object with a lifetime shorter than the A object referencing it, but it's a step in the right direction and may catch some accidental/careless abuses. (Other answers already discuss design scenarios and alternatives - I won't cover that same ground / whether references can be safe(r) in the illustrated scenario is already an important question.)
No, there is not. Remember that references are pointers under the hood, and normally don't control the lifetime of the object they reference (see here for an exception, although it doesn't apply in this case). I would recommend just having a B object, if this is in a piece of code that you need.
Also, you could utilize an object such as a shared_ptr in C++11, which will only eliminate the object once both the pointer in the function and in the object have been destroyed.

Lifetime of temporaries [duplicate]

This question already has answers here:
Does a const reference class member prolong the life of a temporary?
(6 answers)
Closed 9 years ago.
Below code shows lifetime of object created in function create() is extended to the life time of const ref created in main, is this correct in all cases? I mean we can extend the life time of temporary in certain cases by creating a reference to it? Or in this specific case the compiler is misbehaving?
It is compiled with MSVC2005
#include <iostream>
class testClass
{
public:
testClass()
{
std::cout << "in testClass " << ((void*)this) << std::endl;
}
~testClass()
{
std::cout << "in ~testClass " << ((void*)this) << std::endl;
}
};
testClass create()
{
return testClass();
}
int main()
{
{
testClass const& obj = create();
std::cout << "we got a const reference to obj " << ((void*)&obj) << std::endl;
}
return 0;
}
Output
in testClass 0018FF13
we got a const reference to obj 0018FF13
in ~testClass 0018FF13
Of course other may get different addresses...In above case i was expecting destructor for the object created with function create(), will be called before line
std::cout << "we got a const reference to obj " << ((void*)&obj) << std::endl;
is executed.
This is a special case: binding a const reference to a temporary object, stretches its lifetime until that const reference goes out of scope. This is only true for function local const references, e.g. the following will not work:
struct X
{
int const& i
X(int const& i_) : i(i_) {}
};
int f();
int main()
{
X x(f());
int u = x.i; //!
}
During construction of x, the i_ will be bound to the temporary returned by f, as will i, but although it's a const reference, that temporarie's lifetime will not be stretched to that of i, i.e. the rule does apply here.
See this GOTW article
Update: as is mentioned in the article and in the comments, the const is vital. The C++ standard allows binding of temporaries only to const lvalue references and rvalue references, so int& i = f(); is not allowed. However, MSVC has an extension that allows this, and as with other references, the lifetime of the temporary is extended until the reference goes out of scope. I would not recommend to exploit that extension, as it makes the code nonportable. In fact, I would be careful binding temporaries to references, since this feature is not well known and your colleagues might be baffled seeing it work, which means the code will lack readability.
To clarify - We can show 3 scenarios for testClass create():
1
Returning a copy but catching it by const reference
testClass create()
{
return testClass();
}
testClass const &obj = create();
It extends the life time of temporary testClass() as long as obj.
2
Returning a copy and catching it by assignment (RVO)
testClass create()
{
return testClass();
}
testClass obj = create();
It extends the life time of temporary testClass() as long as obj, because RVO implicitly applies on it. It'd better to say, in fact there is no temporary object here, all things operate on obj even in create() function.
3
Returning a copy and catching it by assignment (without RVO)
testClass create()
{
return testClass();
}
testClass obj = create();
The life time of temporary testClass() exceeds after returning from create(), and a new object comes to world.
This link should help you to understand how this situation is qualified.
When a temporary object is created to initialize a reference variable,
the name of the temporary object has the same scope as that of the
reference variable. When a temporary object is created during the
evaluation of a full-expression (an expression that is not a
subexpression of another expression), it is destroyed as the last step
in its evaluation that lexically contains the point where it was
created.
There are two exceptions in the destruction of full-expressions:
The expression appears as an initializer for a declaration defining an
object: the temporary object is destroyed when the initialization is
complete.
A reference is bound to a temporary object: the temporary
object is destroyed at the end of the reference's lifetime.

Why the reference member that initialized from temporary is still readable? [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
Can a local variable's memory be accessed outside its scope?
C++ constructor: garbage while initialization of const reference
This question is directly related to another question that I've asked a time ago: "Opaque reference instead of PImpl. Is it possible?".
Let's say we have a class with a reference member of some other class which is initialized to a temporary variable in a constructor:
#include <iostream>
struct B
{
B(int new_x = 10) : x(new_x) { std::cout << "B constructed\n"; }
~B() { std::cout << "B destroyed\n"; }
public:
int x;
};
struct A
{
A()
: b( B(23) )
{
std::cout << "A constructed\n";
}
void Foo()
{
std::cout << "A::Foo()\n";
}
~A()
{
std::cout << "A destroyed\n";
}
public:
const B& b;
};
int main()
{
A a;
a.Foo();
cout << "x = " << a.b.x << endl;
}
When I run the code above, the output is:
B constructed
B destroyed
A constructed
A::Foo()
x = 23
A destroyed
It seems that even if temporary is destroyed so the reference member should be invalid, the integer field of the reference member is still readable. Why does it still work?
Undefined behaviour. It so happens in your case that the memory previously occupied by the temporary B is not overwritten before you reference it. Next time you run the program, anything could happen.
Note that the superficially similar
const B &b = B();
does have defined behaviour; the lifetime of the temporary B is extended through reference binding. This only applies to reference variables, not reference members.
If a reference is invalid, that doesn't mean it's unreadable. It means that it doesn't refer to a valid object. It may or may not refer to some piece of accessible memory; if it does, you may or may not find that the memory contains the remnants of whatever object used to be there.
To summarise, the behaviour is undefined.
The memory that has been allocated to the temporary B is now invalid, but it has not been put to any other use. That is why your read produces the value that was there the last time. However, this is undefined behavior. Running valgrind should pinpoint the place of the error.
If you are wondering "how can it be", here is a great answer explaining what happens in a very similar situation.

const reference public member to private class member - why does it work?

Recently, I found an interesting discussion on how to allow read-only access to private members without obfuscating the design with multiple getters, and one of the suggestions was to do it this way:
#include <iostream>
class A {
public:
A() : _ro_val(_val) {}
void doSomething(int some_val) {
_val = 10*some_val;
}
const int& _ro_val;
private:
int _val;
};
int main() {
A a_instance;
std::cout << a_instance._ro_val << std::endl;
a_instance.doSomething(13);
std::cout << a_instance._ro_val << std::endl;
}
Output:
$ ./a.out
0
130
GotW#66 clearly states that object's lifetime starts
when its constructor completes successfully and returns normally. That is, control reaches the end of the constructor body or an earlier return statement.
If so, we have no guarantee that the _val memeber will have been properly created by the time we execute _ro_val(_val). So how come the above code works? Is it undefined behaviour? Or are primitive types granted some exception to the object's lifetime?
Can anyone point me to some reference which would explain those things?
Before the constructor is called an appropriate amount of memory is reserved for the object on Freestore(if you use new) or on stack if you create object on local storage. This implies that the memory for _val is already allocated by the time you refer it in Member initializer list, Only that this memory is not properly initialized as of yet.
_ro_val(_val)
Makes the reference member _ro_val refer to the memory allocated for _val, which might actually contain anything at this point of time.
There is still an Undefined Behavior in your program because, You should explicitly initialize _val to 0(or some value,you choose)in the constructor body/Member Initializer List.The output 0 in this case is just because you are lucky it might give you some other values since _val is left unInitialized. See the behavior here on gcc 4.3.4 which demonstrates the UB.
But as for the Question, Yes indeed the behavior is Well-Defined.
The object's address does not change.
I.e. it's well-defined.
However, the technique shown is just premature optimization. You don't save programmers' time. And with modern compiler you don't save execution time or machine code size. But you do make the objects un-assignable.
Cheers & hth.,
In my opinion, it is legal (well-defined) to initialize a reference with an uninitialized object. That is legal but standard (well, the latest C++11 draft, paragraph 8.5.3.3) recommends using a valid (fully constructed) object as an initializer:
A reference shall be initialized to refer to a valid object or function.
The next sentence from the same paragraph throws a bit more light at the reference creation:
[Note: in particular, a null reference cannot exist in a well-defined program, because the only way to create such a reference would be to bind it to the “object” obtained by dereferencing a null pointer, which causes undefined behavior.]
I understand that reference creation means binding reference to an object obtained by dereferencing its pointer and that probably explains that the minimal prerequisite for initialization of reference of type T& is having an address of the portion of the memory reserved for the object of type T (reserved, but not yet initialized).
Accessing uninitialized object through its reference can be dangerous.
I wrote a simple test application that demonstrates reference initialization with uninitialized object and consequences of accessing that object through it:
class C
{
public:
int _n;
C() : _n(123)
{
std::cout << "C::C(): _n = " << _n << " ...and blowing up now!" << std::endl;
throw 1;
}
};
class B
{
public:
// pC1- address of the reference is the address of the object it refers
// pC2- address of the object
B(const C* pC1, const C* pC2)
{
std::cout << "B::B(): &_ro_c = " << pC1 << "\n\t&_c = " << pC2 << "\n\t&_ro_c->_n = " << pC1->_n << "\n\t&_c->_n = " << pC2->_n << std::endl;
}
};
class A
{
const C& _ro_c;
B _b;
C _c;
public:
// Initializer list: members are initialized in the order how they are
// declared in class
//
// Initializes reference to _c
//
// Fully constructs object _b; its c-tor accesses uninitialized object
// _c through its reference and its pointer (valid but dangerous!)
//
// construction of _c fails!
A() : _ro_c(_c), _b(&_ro_c, &_c), _c()
{
// never executed
std::cout << "A::A()" << std::endl;
}
};
int main()
{
try
{
A a;
}
catch(...)
{
std::cout << "Failed to create object of type A" << std::endl;
}
return 0;
}
Output:
B::B(): &_ro_c = 001EFD70
&_c = 001EFD70
&_ro_c->_n = -858993460
&_c->_n = -858993460
C::C(): _n = 123 ...and blowing up now!
Failed to create object of type A