避免返回参考引用参数 [英] Avoid returning by-reference argument
问题描述
假设我有一个类 Option
:
template< typename T>
class Option {
public:
Option()noexcept
{}
选项(T val)noexcept:val_(std :: make_shared< T> ;(std :: move(val)))
{}
const T& get()const
{
if(val_ == nullptr){
throw std :: out_of_range(get on empty Option);
}
return * val_;
}
const T& getOrElse(const T& x)const
{
return val_ == nullptr? x:* val_;
}
private:
std :: shared_ptr< T> val_;
};
参数传递给 Option :: getOrElse
是当此选项
为空时返回的默认值:
; int> X; // empty
int y = 123;
x.getOrElse(y); // == 123
但是,我认为以下代码不安全:
选项< int> X;
x.getOrElse(123); //引用临时变量!
一种更安全的方法是通过 Option :: getOrElse
,但是当 Option
不为空时,这将是浪费。
更新2:也许这个?
T getOrElse(T& x)const {...}
const T& getOrElse(const T& x)const {...}
但我认为这可能是模糊的因为左值和右值参数都适合第二个版本。
不安全:
选项< int> X;
x.getOrElse(123); //引用临时变量!
这是为什么 std :: optional :: value_or()
返回 T
而不是 T&
或 T const&
。根据 N3672 :
有人认为函数应该返回常量引用而不是值,这样可以避免某些情况下的复制开销:
void observe(const X& x);
可选< X> ox {/ * ... * /};
observe(ox.value_or(X {args})); //不必要的副本
但是,函数value_or的好处仅在提供可选对象时可见作为临时(没有名称);否则,三元运算符同样有用:
可选< X> ox {/ * ... * /};
observe(ox?* ok:X {args}); // no copy
此外,通过引用返回将很可能渲染悬挂引用,可选对象被释放,因为第二个参数通常是临时的:
可选< X> ox {nullopt};
auto&&& x = ox.value_or(X {args});
cout<< X; // x is dangling!
我建议您遵循相同的指南。如果你真的需要避免复制,使用三元。这是安全无恙的:
可选< int> ox = ...;
const int& val = ox? * ox:123;
如果你真的没有,或 Optional
是一个右值, getOrElse()
更简洁。
Suppose I have a class Option
:
template<typename T>
class Option {
public:
Option() noexcept
{}
Option(T val) noexcept : val_(std::make_shared<T>(std::move(val)))
{}
const T & get() const
{
if (val_ == nullptr) {
throw std::out_of_range("get on empty Option");
}
return *val_;
}
const T & getOrElse(const T &x) const
{
return val_ == nullptr ? x : *val_;
}
private:
std::shared_ptr<T> val_;
};
The argument passed to Option::getOrElse
is the default value to return when this Option
is empty:
Option<int> x; // empty
int y = 123;
x.getOrElse(y); // == 123
However, I think the following code is not safe:
Option<int> x;
x.getOrElse(123); // reference to temporary variable!
A safer way would be to return by value from Option::getOrElse
, but that would be wasteful when the Option
is non-empty. Can I work around this somehow?
UPDATE: I'm thinking about perhaps overloading on the argument type (lvalue/rvalue) of getOrElse
, but haven't figured out exactly how to do so.
UPDATE 2: Maybe this?
T getOrElse(T &&x) const { ... }
const T & getOrElse(const T &x) const { ... }
But I think this might be ambiguous because both lvalue and rvalue arguments fit the second version.
However, I think the following code is not safe:
Option<int> x; x.getOrElse(123); // reference to temporary variable!
You are correct. This is why std::optional::value_or()
returns a T
and not a T&
or T const&
. As per the rationale in N3672:
It has been argued that the function should return by constant reference rather than value, which would avoid copy overhead in certain situations:
void observe(const X& x); optional<X> ox { /* ... */ }; observe( ox.value_or(X{args}) ); // unnecessary copy
However, the benefit of the function value_or is only visible when the optional object is provided as a temporary (without the name); otherwise, a ternary operator is equally useful:
optional<X> ox { /* ... */ }; observe(ox ? *ok : X{args}); // no copy
Also, returning by reference would be likely to render a dangling reference, in case the optional object is disengaged, because the second argument is typically a temporary:
optional<X> ox {nullopt}; auto&& x = ox.value_or(X{args}); cout << x; // x is dangling!
I suggest you follow the same guidelines. If you really need to avoid the copy, use a ternary. This is safe and copyless:
Optional<int> ox = ...;
const int& val = ox ? *ox : 123;
If you really don't, or the Optional
is an rvalue anyway, getOrElse()
is more concise.
这篇关于避免返回参考引用参数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!