按值返回和通过 const 引用传递时避免临时构造

Posted

技术标签:

【中文标题】按值返回和通过 const 引用传递时避免临时构造【英文标题】:Avoiding temporary construction when returning by value and passing by const reference 【发布时间】:2020-12-30 01:28:13 【问题描述】:

我有一个通过 const 引用接受 Large 的函数:

void func(const Large& param);

还有一个拥有Large的类:

class HoldsLarge 
public:
  Large GetByValue() const  return l; ;

private:
  Large l;

如果我这样做

HoldsLarge x;
func(x.GetByValue());

我是否正确理解将为x.GetByValue() 构造一个临时副本,该副本将通过引用func 传递?标准中有什么东西可以让编译器完全省略临时的构造吗?毕竟func 只需要一个constHoldsLarge::l 的引用。

我知道我可以通过 const 引用简单地返回 HoldsLarge::l,但我想防止客户端意外创建悬空引用。

【问题讨论】:

无法更改签名是无法避免的。你为什么不做另一个函数GetByConstReference? (或者使用智能指针weak_ptr ... 同样,对于线程,完全有可能在函数运行时修改原始x.l。即使没有,也可能在外部引用它(作为全局变量或其他东西),func 可能会修改其中一个。所以这是一种行为改变。 我觉得对悬空引用的担心被夸大了。有没有人担心std::vector<T>::operator[] 提供参考资料(或者,你知道,.)? AFAIK,不允许编译器进行这种“优化”。如果它这样做了,那么该函数可能会通过使用const_cast 来更改l 的状态。是的,const_cast 不好,但它总是被错误地使用。 @Jarod42 如果x.GetByValue() 按值返回,那么(如果我正确阅读标准),当您像func(x.GetByValue()) 一样使用它时,因为func 需要const&x.GetByValue()的返回值确实构造成const对象:“如果转换后的初始化器是prvalue,则其类型T4调整为cv1 T4类型”,而cv1来自引用,它是const。然后是UB来修改那个临时对象,因为那个对象是const,那个对象是const,因为它被构造来绑定的引用类型是const 【参考方案1】:

在少数情况下允许编译器更改行为(作为优化):对于 NRVO,以及从 C++14 开始的新表达式。

你不是那种情况。

然后;只要可观察到的行为相同,as-if 规则就允许进行任何优化。

所以编译器可以进行优化只有在不会改变行为。

不知道func,它不能安全地做到这一点。

func 可能可以通过其他方式(作为全局)访问x.l(或别名)。

例如,关注func 将禁止更改。

Large* largePtr; // possibly set to &x.l by any way
void func(const Large& param)

    print(param.x);
    //param might be alias of largePtr
    mutate(alias->x);
    print(param.x); // const ref should see the modification
                    // whereas copy don't

【讨论】:

以上是关于按值返回和通过 const 引用传递时避免临时构造的主要内容,如果未能解决你的问题,请参考以下文章

为啥向量被视为按值传递,即使它是通过 const 引用传递的?

按值传递比通过 const 引用传递更快的经验法则?

复制赋值运算符应该通过 const 引用还是按值传递?

构造拷贝构造赋值析构

我想知道临时对象(name_compare())是按值传递还是通过引用 std::sort

我应该通过 const 引用传递一个 lambda。