简体   繁体   English

调用左值引用构造函数而不是rvalue引用构造函数

[英]Lvalue reference constructor is called instead of rvalue reference constructor

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 ? 为什么在G类的构造函数中调用F(F&)构造函数而不是F(F&&)构造函数? The parameter for constructor of class G is F&& f which is rvalue reference but constructor for lvalue reference is called. G类构造函数的参数是F&& f ,它是rvalue引用,但是调用了左值引用的构造函数。

Why F(F&) constructor is called instead of F(F&&) constructor in constructor of class G? 为什么在G类的构造函数中调用F(F&)构造函数而不是F(F &&)构造函数?

Because f is an lvalue. 因为f是左值。 Even though it is bound to an rvalue, and its type is rvalue reference to F , it is also a named variable . 即使它绑定到一个rvalue,它的类型是对F rvalue引用,它也是一个命名变量 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: 如果要仅捕获rvalues,则应按如下所示更改代码:

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: 或者,您可以使用std::forward<>() ,这在本例中是等效的,但是使您转发 f的意图更加清晰:

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 : 现在,最后一个定义很容易扩展,因此F类型的左值和右值都可以绑定到参数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: 例如,这允许以这种方式构造G的实例:

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. 由于非模板函数优于函数模板的实例化,因此在从另一个G对象构造G对象时将选择复制构造函数。 The same applies, of course, to the move constructor. 当然,这同样适用于移动构造函数。 This is left as an exercise. 这是一个练习。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM