调用左值引用构造函数而不是右值引用构造函数 [英] Lvalue reference constructor is called instead of rvalue reference constructor

查看:286
本文介绍了调用左值引用构造函数而不是右值引用构造函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

有此代码:

#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;
}

输出为:

F(F&)
G()

为什么在类G的构造函数中调用F(F&)构造函数而不是F(F&&)构造函数?类G的构造函数的参数是F&& f,它是右值引用,但调用了左值引用的构造函数.

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.

推荐答案

为什么在类G的构造函数中调用F(F&)构造函数而不是F(F&&)构造函数?

Why F(F&) constructor is called instead of F(F&&) constructor in constructor of class G?

因为f是左值.即使它与右值绑定,并且其类型是对F的右值引用,它还是一个命名变量.这使其成为左值.不要忘记对象的值类别不是由其类型决定的,反之亦然.

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;
    }
};

或者,您可以使用std::forward<>(),在这种情况下,它是等效的,但是可以使转发 f的意图更加明确:

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;
    }
};

现在,最后一个定义易于扩展,因此类型为F的左值和右值都可以绑定到参数f:

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;
    }
};

例如,这允许通过以下方式构造G的实例:

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

最后一个版本具有 caveat :您的构造函数 基本上也将用作复制构造器 ,因此您可能希望显式定义所有可能的复制构造器,以免出现尴尬的情况:

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;

由于非模板函数优先于函数模板的实例化,因此在从另一个G对象构造G对象时,将选择复制构造函数.当然, move 构造函数也是如此.这留作练习.

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.

这篇关于调用左值引用构造函数而不是右值引用构造函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆