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.
Related
I am with doubt regarding clean code/coding style, the doubt is, when should I use the keyword const in C++ constructor parameters. For example, consider the following class:
class A {
public:
B& b;
explicit A (const B& b_): b(b_) {}
};
In this code, I want to initialize the reference b from the class A, and at the same time, I want to express that the reference passed as a parameter to the class A constructor will not be used to change the values of the object b_. However, the presented code will not compile, and the compiler will present the message:
"error: binding reference of type ‘B&’ to ‘const B’ discards qualifiers"
I know I can use the keyword const in the b class attribute, however, it will make the attribute constant, and I do not want to impose this constraint over the attribute. So, I would like to know what should I do in this situation.
I want to express that the reference passed as a parameter to the class A constructor will not be used to change the values of the object b_.
But that's not true. The reference passed to A's constructor can be modified. Not necessarily by A's constructor directly, but by whatever other member functions of A which modify the object.
If a function takes a parameter by const&, the caller of that function has the right to expect that the object will not be modified by that function or by any process dependent on that function's execution. Such as initializing a reference member in a constructor which can be accessed after that constructor's execution.
What you should do is not lie to the user. A dependent result of A's constructor is that the parameter passed to it may be modified. So it should be a non-const reference.
Equally importantly, the act of creating an A fundamentally depends on the given object continuing to exist after the constructor exits. Since a const& parameter can be bound to a temporary, it would be very easy for someone to call your constructor like this A a(B{}); a after this statement references an object that has been destroyed. If your function took a non-const reference, this would be a compile error.
I am trying to initialize a rvalue reference member in the following situations: struct A is a aggregate and class B has a user defined constructor so it is not an aggregate. According to cppreference here,
struct A {
int&& r;
};
A a1{7}; // OK, lifetime is extended
A a2(7); // well-formed, but dangling reference
The reference in my struct A should be correctly initialized and the temporary string should be extended, but it's not.
For my class B, in the same cppreference page:
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).
(until C++14)
But I am still getting problem with MSVC with /std:c++latest. Am I missing anything?
struct A
{
std::string&& ref;
};
class B
{
std::string&& ref;
public:
B(std::string&& rref):ref(std::move(rref)){}
void print() {std::cout<<ref<<'\n';}
};
int main()
{
A a{ std::string{"hello world"} };
std::cout<<a.ref<<'\n'; //garbage in MSVC with c++latest, correct in GCC9.2
B b{ std::string{"hello world"} };
b.print(); //same
}
EDIT: Please tell me if I am getting dangling reference in these two cases in the first place. And for MSVC, the version is the latest update, v19.24.28315
EDIT2: OK I am actually confused by cppreference's explanation. I am not asking specificly for C++20. Please tell me under which C++ version, the code is well-formed or not.
EDIT3: Is there a proper way to initialize a rvalue reference member of a non-aggregate class? Since bind it to a temporary in a member initializer list is ill-formed (until C++14, does it mean it is good since C++14?) and passing a temporary to a constructor expecting an rvalue cannot extend its lifetime twice.
Your class A is an aggregate type, but B isn't, because it has a user-provided constructor and a private non-static member.
Therefore A a{ std::string{"hello world"} }; is aggregate initialization which does extend the lifetime of the temporary through reference binding to that of a.
On the other hand B is not aggregate initialization. It calls the user-provided constructor, which passes on the reference. Passing on references does not extend the lifetime of the temporary. The std::string object will be destroyed when the constructor of B exits.
Therefore the later use of a has well-defined behavior, while that of b will have undefined behavior.
This holds for all C++ versions since C++11 (before that the program would be obviously ill-formed).
If your compiler is printing garbage for a (after removing b from the program so that it doesn't have undefined behavior anymore), then this is a bug in the compiler.
Regarding edit of question:
There is no way to extend the lifetime of a temporary through binding to a reference member of a non-aggregate class.
Relying on this lifetime extension at all is very dangerous, since you would likely not get any error or warning if you happen to make the class non-aggregate in the future.
If you want the class to always retain the object until its lifetime ends, then just store it by-value instead of by-reference:
class B
{
std::string str;
public:
B(std::string str) : str(std::move(str)) {}
void print() { std::cout << str << '\n'; }
};
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;
I dont understand why the following code doesn't work:
#include <iostream>
using namespace std;
class PayOff{};
class PayOffCall : public PayOff{};
class PayOffBridge{
public:
PayOffBridge( PayOff& innerPayOff){};
};
class VanilaOption{
public:
VanilaOption(PayOffBridge& ThePayOff_){cout << " test " <<endl;}
};
int main () {
PayOffCall thePayOff;
VanilaOption theOption(thePayOff);
return 0;
}
For some reason changing the reference to const in VanilaOption class (code below) makes it work, can someone explain how does it work? The error I get is: no matching function for call to 'VanilaOption::VanilaOption(PayOffCall&)'
but it doesn't help me to figure it out.
class VanilaOption{
public:
VanilaOption(const PayOffBridge& ThePayOff_){cout << " test " <<endl;}
};
I also don't understand why passing PayOffCall reference when PayOffBridge reference is expected works, can someone help me out on this?
Thank you!
The original code doesn't work because thePayOff isn't a value of type PayOffBridge, but rather of type PayOffCall.
The modified code works because it allows the construction of a temporary PayOffBridge object from the PayOff subobject of the PayOffCall object, and then construct the VanillaOption object from that temporary. That is because:
the PayOffBridge constructor is non-explicit,
an lvalue of a derived class can be converted to an lvalue of any of its accessible base classes, and
because temporaries can bind to const lvalue references (but not to non-const lvalue references).
In other words, the const reference version allows code like:
VanilaOption theOption(PayOffBridge(thePayOff));
// ^^^^^^^^^^^^^^^^^^^^^^^-----< temporary
Whereas for the non-const version you'd need a mutable lvalue, perhaps like:
PayOffBridge b(thePayOff);
VanilaOption theOption(b);
// ^----- lvalue
A PayoffCall (like thePayoff variable) is not a PayOffBridge, so it can't be bound to a reference to PayOffBridge.
However, it can be converted to PayOffBridge, via the converting PayOffBridge constructor.
With a reference to const argument such a conversion is performed, producing a temporary that's bound to the formal argument.
A reference to const can in general be bound to a temporary.
An ordinary reference to non-const can't be bound to a temporary.
Class A
{
A(B& b) : mb(b)
{
// I will not access anything from B here
}
B& mb;
};
Class B
{
B(): a(*this)
{}
A a;
}
I run into such a situation may times, the contained object needs to use the containers functionality. Having a reference to the container object in the contained object seems to be the best way to do this. Of course, I could do this with a pointer, that way I could have a setter setB(B* b) {mb = b;} which I could call later after I am sure B is initialized but I would much prefer to do this with a reference which means I need to initialize it in the constructor, hence the problem.
Since you're only initializing the reference to B, this should be just fine -- by the time B's constructor runs the memory location for it has already been setup.
Do keep in mind that you can't call any methods in B safely from A's constructor because B has not finished constructing yet.
The appropriate quote from the standard is:
§3.8 [basic.life]/6
Similarly, before the lifetime of an object has started but after the storage which the object will occupy has been allocated or, after the lifetime of an object has ended and before the storage which the object occupied is reused or released, any lvalue which refers to the original object may be used but only in limited ways. Such an lvalue refers to allocated storage (3.7.3.2), and using the properties of the lvalue which do not depend on its value is well-defined. If an lvalue-to-rvalue conversion (4.1) is applied to such an lvalue, the program has undefined behavior; if the original object will be or was of a non-POD class type, the program has undefined behavior if:
— the lvalue is used to access a non-static data member or call a non-static member function of the object, or
— the lvalue is implicitly converted (4.10) to a reference to a base class type, or
— the lvalue is used as the operand of a static_cast(5.2.9) (except when the conversion is ultimately to char& or unsigned char&), or
— the lvalue is used as the operand of a dynamic_cast(5.2.7) or as the operand oftypeid.
It depends what you are doing in the A constructor.
The B object is not fully constructed until the constructor returns. Furthermore, before you enter the body of the B constructor, the objects within the B object may not be fully constructed. For example:
class B
{
A a;
std::string str;
public:
B() : a(*this)
{
}
};
At the time that A::A is called, str is not yet constructed. If you try to use str within A::A (either directly or indirectly), you will have undefined behavior.