【问题标题】:Lvalue reference constructor is called instead of rvalue reference constructor调用左值引用构造函数而不是右值引用构造函数
【发布时间】:2013-03-09 19:07:09
【问题描述】:

有这个代码:

#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&amp;)构造函数而不是F(F&amp;&amp;)构造函数? G 类的构造函数的参数是F&amp;&amp; f,它是右值引用,但调用了左值引用的构造函数。

【问题讨论】:

    标签: c++ c++11 move-semantics rvalue-reference


    【解决方案1】:

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

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

    当您将左值传递给函数时,只能将左值引用绑定到它。如果您只想捕获右值,则应按如下方式更改代码:

    class G {
        F f_;
    public:
        G(F&& f) : f_(std::move(f)) {
           std::cout << "G()" << std::endl;
        }
    };
    

    或者,您可以使用std::forward&lt;&gt;(),这在这种情况下是等价的,但会使您转发 f 的意图更加清晰:

    class G {
        F f_;
    public:
        G(F&& f) : f_(std::forward<F>(f)) {
           std::cout << "G()" << std::endl;
        }
    };
    

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

    class G {
        F f_;
    public:
        template<typename F>
        G(F&& f) : f_(std::forward<F>(f)) {
           std::cout << "G()" << std::endl;
        }
    };
    

    例如,这允许以这种方式构造G 的实例:

    F f;
    G g(f); // Would not be possible with a constructor accepting only rvalues
    

    最后一个版本有一个警告:你的构造函数will basically work as a copy-constructor as well,所以你可能想显式定义所有可能的复制构造函数以避免尴尬的情况:

    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 构造函数。这留作练习。

    【讨论】:

    • @AndyProwl:我说的是:stacked-crooked.com/…。阅读 cmets。
    • @Nawaz:哦,是的,我现在明白你的意思了。你是对的,我应该同时声明显式复制构造函数的const 和非const 版本,否则无论如何都会选择构造函数模板。已编辑,谢谢。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2012-07-19
    • 1970-01-01
    • 1970-01-01
    • 2020-09-20
    • 2013-08-07
    • 2012-05-09
    相关资源
    最近更新 更多