【问题标题】:C++ const lvalue referencesC++ 常量左值引用
【发布时间】:2011-05-07 16:15:26
【问题描述】:

假设我有:

  • A 类不可复制
  • B 类的成员为 const A& a(并在其构造函数中采用 A 并将其设置在其初始化列表中)
  • 一个函数A GenerateA();

这是否意味着它应该是有效的: B(生成A()) ?

即,const ref 是否意味着 generateA() 返回的 A 的副本没有完成?这是否意味着只要 B 存在,返回的临时范围就会扩展?

编辑:来自 cmets 的附加问题: 如果左值是 const A&,是否可以将 GenerateA() 中的 A& 返回到本地 A?

谢谢!

【问题讨论】:

  • 您不能持有对生命周期已结束的值的引用;这正是您返回对局部变量的引用时发生的情况。
  • @GMan:这本质上是我的问题的意图,即使用 const 引用是否会改变所述对象的生命周期。

标签: c++ reference constants noncopyable


【解决方案1】:

如果A 不可复制,则函数A GenerateA() 无效,因为按值返回需要创建副本。

如果函数返回一个引用(即A &GenerateA()),并且该引用指向本地创建的A 对象,则函数一退出它就会失效。 C++ 没有任何形式的垃圾回收,因此只要对象在使用中,就无法“延长”它的生命周期。

【讨论】:

  • 您能详细说明一下吗?是否需要副本,因为它必须将对象获取到正确的堆栈帧?如果左值是 const A&,将 A& 从 GenerateA() 返回到本地 A 是否可以接受?谢谢!
  • @John Bind:堆栈框架依赖于实现,不会影响语言的工作方式。按值返回的语义是创建了一个副本——即使编译器可以对此进行优化,如果无法复制对象,它仍然是一个错误。
  • @casablanca:很高兴知道。谢谢你。旁白:“编译器可以优化”== RVO?
  • @John Bind:即使在 RVO 的情况下,也必须尊重复制语义
  • @casablanca :“只要对象在使用,就无法“延长”对象的生命周期”,这实际上并不完全正确,请参阅我的回答
【解决方案2】:

正如其他人所说,如果A不可复制,A GenerateA()无法编译。

关于 const ref :不,临时的生命周期不会延长到 B 的生命周期。标准 [12.2.5] 规定:

临时绑定到构造函数的 ctor-initializer (12.6.2) 中的引用成员,直到构造函数退出。 [...] 在函数返回语句 (6.6.3) 中与返回值的临时绑定一直存在,直到函数退出。

所以是的,在某些情况下存在临时生命周期的延长(并且有时确实有用:see this article),但在您提出的情况中不存在。

关于您的最后一个问题,从 GenerateA() 返回对局部变量的引用是不合法的(并且将结果绑定到 const 引用不会有任何帮助)。

【讨论】:

    【解决方案3】:

    是和否。

    是的,const 引用将绑定到临时变量。不,作为类成员的 const 引用不会像具有自动持续时间的 const 引用那样延长生命周期。

    【讨论】:

    • 您能指出标准中的相关部分吗?而且,它不起作用。编译器(应该,gcc 确实)抱怨为对象 A 构造的副本是私有的。
    • @John:这是因为 A 是由来自 generateA() 的副本返回的,而不是由其余代码返回的。该标准需要一个可访问的复制构造函数(或者最终是一个移动构造函数)来调用generateA()
    【解决方案4】:

    这是一个示例

    #include <iostream>
    using namespace std;
    
    int& GenX(bool reset)
    {
        static int* x = new int;
        *x = 100;
        if (reset)
        {
            delete x;
            x = new int;
            *x = 200;
        }
        return *x;
    }
    
    class YStore
    {
    public:
        YStore(int& x);
        int& getX() { return my_x; }
    private:
        int& my_x;
    };
    
    YStore::YStore(int& x)
     : my_x(x)
    {
    }
    
    int main()
    {
        YStore Y(GenX(false));
        cout << "X: " << Y.getX() << endl;
        GenX(true); // side-effect in Y
        cout << "X: " << Y.getX() << endl;
        return 0;
    }
    

    输出:

    X: 100
    X: 200
    

    【讨论】:

    • 这违背了使用引用的目的,并造成内存泄漏。
    • 为什么将 x 改为 200? GenX 不返回一个 int*,它返回一个 int&,这应该意味着您第二次调用 GetX() 时应该从空闲内存中读取,但是,您得到的是 200?有什么关于我缺少的静态的东西吗?
    • 它与引用如何绑定到值有关。当我们返回对值的引用时,本质上我们只是返回 x。 x 是一个静态指针,因此它在程序的生命周期内都存在。当我们用一个新的内存位置更新 x 时,我们会同时更新持有对 x 的引用的任何人。
    • 但是你没有返回 X,你返回的是 *x,它应该是取消引用的指针(所以位置 x 的实际值),应该在你删除后释放?
    • @Will Bickford:详细地说,如果我们返回 x 本身而不是 *x,我希望这种行为。我觉得我遗漏了一些明显的东西,我应该打开一个新问题吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-05-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-06
    • 2014-03-13
    • 1970-01-01
    相关资源
    最近更新 更多