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.
Related
I'm studying about copy constructors now. I learned that copy constructor is called when we make a object with already made object. And I heard that When we use object as a argument in function, copy constructor is called.
I want to know what happens inside function. How can function knows that function have to use copy constructor?
I think inside function, the passed argument is assigned to function parameter so that copy constructor is called.
I think inside function, the passed argument is assigned to function parameter
No. The arguments are essentially local variables of the called function, except they are created by the caller before the execution goes into the function. The function does not even know how they were constructed.
why does function use copy constructor?
So this assumption is wrong, the function does not use copy constructor on its arguments. The caller does (if argument is a variable and passed by value, so copy needs to be made).
How can function knows that function have to use copy constructor?
When passing the argument by value, the parameter is initialized by the passed argument. And from copy constructor's documentation:
The copy constructor is called whenever an object is initialized (by direct-initialization or copy-initialization) from another object of the same type (unless overload resolution selects a better match or the call is elided), which includes :
function argument passing: f(a);, where a is of type T and f is void f(T t);
So, since the passed argument is used to initialize the parameter(in case of pass by value), according to the above quoted statement, the copy constructor will be used.
Do note that in C++, assignment and initialization are different.
Additionally, note that move constructor can also be used instead of copy constructor in case the argument is an rvalue. From move constructor's documentation:
The move constructor is typically called when an object is initialized (by direct-initialization or copy-initialization) from rvalue (xvalue or prvalue) (until C++17)xvalue (since C++17) of the same type, including
function argument passing: f(std::move(a));, where a is of type T and f is void f(T t);
First of all, it's inaccurate to say that a copy constructor is called.
Let me give some examples:
void foo(string b);
...
{
string a = "asdf";
foo(a); // copy constructor is called to make string b
foo(std::move(a)); // move constructor is called to make string b
foo("asdfg"); // the string b is constructed inplace from "asdfg"
}
Basically, foo has the string as a local parameter and it needs to be constructed in some way. How exactly it is constructed is defined by the call.
I am trying to understand C++11 rvalue references and how to use them for optimal performance in my code.
Let's say we have a class A that has a member pointer to a large amount of dynamically allocated data.
Furthermore, a method foo(const A& a) that does something with an object of class A.
I want to prevent the copy constructor of A from being called when an object of A is passed to the function foo, since in that case it will perform a deep copy of the underlying heap data.
I tested passing an lvalue reference:
A a;
foo(a);
and passing an rvalue reference:
foo(A());
In both cases the copy constructor was not called.
Is this expected or is this due to some optimization of my compiler (Apple LLVM 5.1)? Is there any specification about this?
That is expected. If you pass an argument to a reference type parameter (whether lvalue or rvalue reference), the object will not be copied. That is the whole point of references.
The confusion you're having is pretty common. The choice of copy or move constructor only occurs when passing an object by value. For example:
void foo(A a);
When passing an A object to this function, the compiler will determine whether to use the copy or move constructor depending on whether the expression you pass is an lvalue or rvalue expression.
On the other hand, none of the following functions would even try to invoke the copy or move constructor because no object is being constructed:
void foo(A& a);
void foo(const A& a);
void foo(A&& a);
void foo(const A&& a);
It's important to note that you should rarely (if ever) have any reason to write a function, other than a move constructor/assignment operator, that takes an rvalue reference. You should be deciding between passing by value and passing by const lvalue reference:
If you're going to need a copy of the object inside the function anyway (perhaps because you want to modify a copy or pass it to another function), take it by value (A). This way, if you're given an lvalue, it'll have to be copied (you can't avoid this), but if you're given an rvalue, it'll be optimally moved into your function.
If you're not going to need a copy of the object, take it by const lvalue reference (const A&). This way, regardless of whether you're given an lvalue or rvalue, no copy will take place. You shouldn't use this when you do need to copy it though, because it prevents you from utilising move semantics.
From the sounds of it, you're not going to make any copies at all, so a const A& parameter would work.
I am quite puzzled by the std::move stuff. Assume I have this
piece of code:
string foo() {
string t = "xxxx";
return t;
}
string s = foo();
How many times the string constructor is called? Is it 2 or 3?
Is the compiler going to use move for this line?
string s = foo();
If so, in the function I am not even returning rvalue reference, so how could the
compiler invoke the move constructor?
It depends on the compiler. In this case, the standard requires that there will be at least one constructor call. Namely, the construction of t.
But the standard allows the possibility of two others: the move-construction of the value output of foo from t, and the move-construction of s from the value output of foo. Most decent compilers will forgo these constructors by constructing t directly in the memory for s. This optimization is made possible because the standard allows these constructors to not be called if the compiler chooses not to.
This is called copy/move "elision".
If so, in the function I am not even returning rvalue reference, so how could the compiler invoke the move constructor?
You seem to be laboring under the misconception that && means "move", and that if there's no && somewhere, then movement can't happen. Or that move construction requires move, which also is not true.
C++ is specified in such a way that certain kinds of expressions in certain places are considered valid to move from. This means that the value or reference will attempt to bind to a && parameter before binding to a & parameter. Temporaries, for example, will preferentially bind to a && parameter before a const& one. That's why temporaries used to construct values of that type will be moved from.
If you have a function which returns a value of some type T, and a return expression is of the form return x, where x is a named variable of type T of automatic storage duration (ie: a function parameter or stack variable), then the standard requires that this return expression move construct the returned value from x.
The return value of foo is a temporary. The rules of C++ require that temporaries bind to && parameters before const&. So you get move construction into s.
We have:
vector<int> f(int);
vector<int> v;
This works:
f(x).swap(v);
This doesn't:
v.swap(f(x));
And why?
swap() takes a non-const reference to a vector<int>. A non-const reference cannot bind to an rvalue (a temporary object). A call to a function that returns by value (like f) is an rvalue.
The reason that f(x).swap(v) works is because inside of std::vector<int>::swap, the temporary object returned by f(x) can use this to refer to itself. this is not an rvalue.
You are allowed to call member functions on temporaries but in C++ they cannot be bound to non-const references.
For example:
int &x = 5; // illegal because temporary int(5) cannot be bound to non-const reference x
Actually, (while James' answer is certainly right (and so is Prasoon's), there is some underlying problem to grasp.
When we reduce f(x) to its result y, and y.swap(v) (or v.swap(y), it doesn't matter in this case) to use generalized identifier names, it becomes
y.func(v)
Now, func() being a member function with one argument, it actually has two arguments: what's been passed in as v, and the implicit this pointer every non-static member function receives, here bound to y. Tossing encapsulation aside, every member function called as y.func(v) could be made a non-member function to be called as func(y,v). (And in fact, there actually are a non-member swap() functions. Also, each time you need to overload one of those binary operators that could be overloaded both as members or non-members, you have to make this decision.)
However, there are subtle differences between y.func(v) and func(y,v), because C++ treats the this argument, the argument that's passed by writing it before the . (the dot), different than the other arguments, and it does so in many ways.
As you have discovered, the this argument might be an rvalue (temporary) even for non-const member functions, while for the other arguments, a non-const reference prevents rvalues from being bound to the argument. Also, the this argument's run-time type might influence which function is called (for virtual members), while the other arguments' run-time type is irrelevant, because a function is chosen only depending on their compile-time type only. And implicit conversions are only ever applied to the explicit arguments of a member functions, but never to its implicit this argument. (That's why you can pass a string literal for a const std::string&, but cannot call std::string::size() on a string literal.)
So, to conclude, despite the fact that what's before the . ends up as an (implicit) function argument, it's actually treated very differently from the other function arguments.
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.