我应该通过右值引用返回右值引用参数吗? [英] Should I return an rvalue reference parameter by rvalue reference?

查看:87
本文介绍了我应该通过右值引用返回右值引用参数吗?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个函数,该函数可就地修改std::string&个左值引用,并返回对输入参数的引用:

std::string& transform(std::string& input)
{
    // transform the input string
    ...

    return input;
}

我有一个辅助函数,该函数允许对右值引用执行相同的内联转换:

std::string&& transform(std::string&& input)
{
    return std::move(transform(input)); // calls the lvalue reference version
}

请注意,它返回右值引用.

我已经阅读了关于SO的几个与返回右值引用有关的问题(此处此处),并得出结论,这是不好的做法.

据我所读,似乎已经达成共识,因为返回值个右值,再加上RVO的考虑,仅按值返回就一样有效:

std::string transform(std::string&& input)
{
    return transform(input); // calls the lvalue reference version
}

但是,我还读到返回函数参数会阻止RVO优化(例如此处)

这使我相信,将从transform(...)的左值引用版本的std::string&返回值复制到std::string返回值.

对吗?

保留我的std::string&& transform(...)版本更好吗?

解决方案

没有正确的答案,但是按值返回更安全.

我已经阅读了关于SO的几个与返回右值引用有关的问题,并得出结论,这是不好的做法.

返回对参数的引用会给调用者加一个契约,而后者要么是

  1. 该参数不能为临时参数(这正是右值引用所代表的含义),或者
  2. 在调用者的上下文中(当临时对象被销毁时),返回值将不会保留到下一个分号之后.

如果呼叫者通过了一个临时尝试保存结果,他们将获得一个悬空的引用.

从我阅读的内容来看,似乎共识是,由于返回值是右值,加上RVO的考虑,仅按值返回就一样有效:

按值返回将添加移动构造操作.这样做的成本通常与物体的大小成正比.按引用返回仅要求机器确保一个地址位于寄存器中,而按值返回则需要将参数std::string中的几个指针清零并将其值放入要返回的新std::string中.

便宜,但非零.

令人惊讶的是,标准库当前采取的方向是快速和不安全的,并返回参考. (我知道实际上唯一执行此功能的是<tuple>中的std::get.)碰巧的是,我提出了提案对于要解决此问题的C ++核心语言委员会,修订版正在进行中,而就在今天,我已经开始研究实施情况.但这很复杂,而且不确定.

std::string transform(std::string&& input)
{
    return transform(input); // calls the lvalue reference version
}

编译器不会在此处生成move.如果input根本不是参考,而您做了return input;会,但是没有理由相信transform会因为它是一个参数而返回input,并且不会推断出无论如何,都来自右值引用类型的所有权. (请参阅C ++ 14§12.8/31-32.)

您需要这样做:

return std::move( transform( input ) );

或等效地

transform( input );
return std::move( input );

I have a function which modifies std::string& lvalue references in-place, returning a reference to the input parameter:

std::string& transform(std::string& input)
{
    // transform the input string
    ...

    return input;
}

I have a helper function which allows the same inline transformations to be performed on rvalue references:

std::string&& transform(std::string&& input)
{
    return std::move(transform(input)); // calls the lvalue reference version
}

Notice that it returns an rvalue reference.

I have read several questions on SO relating to returning rvalue references (here and here for example), and have come to the conclusion that this is bad practice.

From what I have read, it seems the consensus is that since return values are rvalues, plus taking into account the RVO, just returning by value would be as efficient:

std::string transform(std::string&& input)
{
    return transform(input); // calls the lvalue reference version
}

However, I have also read that returning function parameters prevents the RVO optimisation (for example here and here)

This leads me to believe a copy would happen from the std::string& return value of the lvalue reference version of transform(...) into the std::string return value.

Is that correct?

Is it better to keep my std::string&& transform(...) version?

解决方案

There's no right answer, but returning by value is safer.

I have read several questions on SO relating to returning rvalue references, and have come to the conclusion that this is bad practice.

Returning a reference to a parameter foists a contract upon the caller that either

  1. The parameter cannot be a temporary (which is just what rvalue references represent), or
  2. The return value won't be retained past the the next semicolon in the caller's context (when temporaries get destroyed).

If the caller passes a temporary and tries to save the result, they get a dangling reference.

From what I have read, it seems the consensus is that since return values are rvalues, plus taking into account the RVO, just returning by value would be as efficient:

Returning by value adds a move-construction operation. The cost of this is usually proportional to the size of the object. Whereas returning by reference only requires the machine to ensure that one address is in a register, returning by value requires zeroing a couple pointers in the parameter std::string and putting their values in a new std::string to be returned.

It's cheap, but nonzero.

The direction currently taken by the standard library is, somewhat surprisingly, to be fast and unsafe and return the reference. (The only function I know that actually does this is std::get from <tuple>.) As it happens, I've presented a proposal to the C++ core language committee toward the resolution of this issue, a revision is in the works, and just today I've started investigating implementation. But it's complicated, and not a sure thing.

std::string transform(std::string&& input)
{
    return transform(input); // calls the lvalue reference version
}

The compiler won't generate a move here. If input weren't a reference at all, and you did return input; it would, but it has no reason to believe that transform will return input just because it was a parameter, and it won't deduce ownership from rvalue reference type anyway. (See C++14 §12.8/31-32.)

You need to do:

return std::move( transform( input ) );

or equivalently

transform( input );
return std::move( input );

这篇关于我应该通过右值引用返回右值引用参数吗?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆