There is this code:
#include <iostream>
class F {
public:
F() = default;
F(F&&) {
std::cout << "F(F&&)" << std::endl;
}
F(F&) {
std::cout << "F(F&)" << std::endl;
}
};
class G {
F f_;
public:
G(F&& f) : f_(f) {
std::cout << "G()" << std::endl;
}
};
int main(){
G g = F();
return 0;
}
The output is:
F(F&)
G()
Why F(F&)
constructor is called instead of F(F&&)
constructor in constructor of class G
? The parameter for constructor of class G
is F&& f
which is rvalue reference but constructor for lvalue reference is called.
Why F(F&) constructor is called instead of F(F&&) constructor in constructor of class G?
Because f
is an lvalue. Even though it is bound to an rvalue, and its type is rvalue reference to F
, it is also a named variable . That makes it an lvalue. Don't forget that the value category of an object is not determined by its type , and vice versa.
When you pass an lvalue to a function, only lvalue references can be bound to it. You should change your code as follows if you want to catch rvalues only:
class G {
F f_;
public:
G(F&& f) : f_(std::move(f)) {
std::cout << "G()" << std::endl;
}
};
Alternatively, you could use std::forward<>()
, which is equivalent in this case, but makes your intent of forwarding f
even clearer:
class G {
F f_;
public:
G(F&& f) : f_(std::forward<F>(f)) {
std::cout << "G()" << std::endl;
}
};
Now this last definition is easy to extend so that both lvalues and rvalues of type F
can be bound to the parameter f
:
class G {
F f_;
public:
template<typename F>
G(F&& f) : f_(std::forward<F>(f)) {
std::cout << "G()" << std::endl;
}
};
This allows, for instance, to construct an instance of G
this way:
F f;
G g(f); // Would not be possible with a constructor accepting only rvalues
This last version has a caveat though: your constructor will basically work as a copy-constructor as well , so you might want to explicitly define all the possible copy constructors to avoid awkward situations:
class G {
F f_;
public:
template<typename F>
G(F&& f) : f_(std::forward<F>(f)) {
std::cout << "G()" << std::endl;
}
G(G const&) = default;
G(G&); // Must be defaulted out-of-class because of the reference to non-const
};
G::G(G&) = default;
Since non-template functions are preferred over instantiations of function templates, the copy constructor will be selected when constructing a G
object from another G
object. The same applies, of course, to the move constructor. This is left as an exercise.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.