【问题标题】:How do I pass a temporary object as a non-const reference into a member function?如何将临时对象作为非常量引用传递给成员函数?
【发布时间】:2021-02-06 18:52:11
【问题描述】:

我们正在创建一个旨在从当前模块发送信息的类(细节与这个问题无关)。这种类型的对象被创建并填充了需要发送的部分数据,然后传递给(不同的类)成员函数。该函数为对象提供其余数据,然后通过对象本身的调用触发发送。因为传入的信息是动态的,所以信息传输对象是一个临时对象,是用最新数据创建的。我们列出的设计在下面的精简源代码中,但 gcc/C++ 不允许这样做,并给出显示的错误。

问题是,我们如何使用可以被调用函数修改和使用的临时对象(避免内存泄漏)来完成预期的行为?

gcc 编译器错误:

infoxfer.cpp: In function ‘int main()’:
infoxfer.cpp:54:43: error: cannot bind non-const lvalue reference of type ‘XferInfo&’ to an rvalue of type ‘XferInfo’
   51 |     callee.doSomething("Something param", XferInfo("from main()"));
      |                                           ^~~~~~~~~~~~~~~~~~~~~~~
infoxfer.cpp:36:62: note:   initializing argument 2 of ‘void Callee::doSomething(const string&, XferInfo&)’
   33 |     void doSomething(const string& somethingParam, XferInfo& xferInfo)
      |                                                    ~~~~~~~~~~^~~~~~~~

提炼的示例代码:
infoxfer.cpp:

#include <iostream>
using std::cout;
using std::endl;

#include <string>
using std::string;

class XferInfo
{
private:
    const string mCallerInfo;
    string mCalleeInfo;

public:
    XferInfo(const string& callerInfo) : mCallerInfo(callerInfo)
    {}

    void setCalleeInfo(const string& calleeInfo)
    {
        mCalleeInfo = calleeInfo;
    }

    void sendData()
    {
        // simulate sending data
        cout << mCallerInfo << " | " << mCalleeInfo << endl;
    }
};

class Callee
{
public:
    void doSomething(const string& somethingParam, XferInfo& xferInfo)
    {
        // complete data for xfer
        xferInfo.setCalleeInfo(somethingParam);

        // simulate doing something
        cout << "do something" << endl;

        // send the complete info
        xferInfo.sendData();
    }
};

int main()
{
    cout << "start" << endl;

    Callee callee;
    callee.doSomething("Something param", XferInfo("from main()"));

    cout << "end" << endl;

    return 0;
}

【问题讨论】:

  • 为什么不简单地按值传递 XferInfo?或者使用右值引用。看来左值引用确实不是您想要的。
  • “使用临时对象(避免内存泄漏很好)”你似乎在这里混淆了概念。内存泄漏很糟糕,但悬空引用同样糟糕(或更糟)。如果按值复制,对象的生命周期将被限制在变量的范围内。没有内存泄漏。
  • callee.doSomething("Something param", XferInfo("from main()")); 更改为XferInfo info("from main()"); callee.doSomething("Something param", info);
  • 编译器必须创建一个XferInfo对象somewhere。按值传递它可以很好地包装它并避免其他潜在问题。

标签: c++ gcc pass-by-reference temporary-objects lvalue-to-rvalue


【解决方案1】:

如 cmets 中所述,您可以简单地将 doSomething 函数更改为接受 rvalue reference 为传递的 XferInfo 对象(使用双精度 &amp;&amp;):

    void doSomething(const string& somethingParam, XferInfo&& xferInfo)
    {
        // complete data for xfer
        xferInfo.setCalleeInfo(somethingParam);
        // ... and so forth ...

从链接的 cppreference 页面:

右值引用可用于延长临时对象的生命周期 对象(注意,对 const 的左值引用可以延长 临时对象也是,但它们不能通过它们修改)

【讨论】:

  • 在这里操作:@Adrian 好的,这 (T&&) 对我来说是新的。需要明确的是,我的理解是在 main() doSomething() 调用中创建的临时 XferInfo 对象被创建,传递给函数调用,然后在返回时被销毁。但是您是说,如果允许使用单个 T& 传递对象,编译器会在传递给函数之前销毁该对象吗?我已经证实它可以在 T&& 下存活。
  • @rtillery 更多的是编译器符合标准的情况。理论上,对临时(非常量)对象的引用作为左值引用传递是不安全的,因此标准不允许这样做。但当然,对象在函数调用中仍然存在是可能的——只是不能保证。使用&amp;&amp;(有点)保证它会。
  • @rtillery IIRC,您还可以有两种不同的doSomething 定义(重载) - 一种带有左值引用(单个 &amp;),另一种带有右值引用(双 &amp;&amp;) .编译器只有在必要时才会选择后者。
  • 使用右值引用定义成员函数并将其用于非临时对象是否有一些缺点?我需要两个重载吗?
  • 对函数使用右值引用确实有一个缺点:非临时对象必须std::move()ed 到函数中。我能找到避免这种情况的唯一方法是使用右值和左值参考版本重载函数。好在右值引用版本只需要调用左值引用版本即可分享代码:void doSomething(const string&amp; somethingParam, XferInfo&amp; xferInfo) {...},void doSomething(const string&amp; somethingParam, XferInfo&amp;&amp; xferInfo) { doSomething(somethingParam, xferInfo); }
猜你喜欢
  • 2019-05-04
  • 2013-01-03
  • 2013-07-10
  • 2021-03-08
  • 2017-07-19
  • 2018-09-18
  • 2013-12-26
  • 1970-01-01
  • 2016-11-08
相关资源
最近更新 更多