按值返回和通过 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
只需要一个const
对HoldsLarge::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 引用传递的?