首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >移动语义与完美转发差异

移动语义与完美转发差异
EN

Stack Overflow用户
提问于 2014-07-14 09:00:59
回答 2查看 3.3K关注 0票数 10

我已经从这个问题中了解到移动语义是什么:什么是移动语义?

但我仍然不明白什么是完美的转发是有关移动语义。

有人能用简单的英语来解释吗?举个简单的例子,完美转发意味着什么?

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2014-07-14 09:46:39

纯英语尝试

这个问题可能太复杂,无法用简单的英语句子准确地描述,但人们可以认为完美转发是一种将传递到另一个函数的临时值转移到另一个函数的方法,就好像第一个函数根本不存在一样,因此没有任何不必要的副本或作业。C++11允许您通过在r值(&&)和l-value (&)引用之间引入一些转换规则来做到这一点,当您试图从中获取引用(r值或l值)时。

R-值引用是C++11的一个特性,它们被设计用于地址移动语义和完善的转发问题()。

这是通俗易懂的解释,但如果你想彻底理解这个问题,我建议你阅读以下内容:

问题是:

我们希望将传递给函数F的一些临时值传递给另一个E ,而不需要任何复制或赋值

试图解决这个问题

  • 如果您试图通过引用传递它,如 模板空洞F(T& a) { E(a);} 你将不能使用临时的(他们不是l-值) F(1,2,3);//行不通
  • 将引用声明为const会延长堆栈上临时引用的生存期(这是历史上为避免常见的悬空引用错误而做的),因此,以下工作如下 模板空洞E(const T& a) {}模板空洞F(const T& a) { E(a);} 但缺点是,您必须修改函数的签名以符合此解决方案
  • 如果我们对E的签名感兴趣(它应该与某物相符),但对F的签名不感兴趣,我们可能会逃脱 模板空洞E(T& a) {}模板空洞F(const & a) { E(const_cast(a));} 但是,如果使用real 调用它并得到非常量‘ed,则会触发未定义的行为
  • 不可维护的解决方案可能是定义所需的所有变体。 模板空洞E(T& a) {}模板空洞F(T& a) { E( a);}模板空洞F(const &a){ E(const_cast(a));} 但是随着参数数量的增加,组合的数量也在增加:--这很可能成为不可维护的

C++11中的解决方案

C++11定义了一些状态的规则

“给定类型TR是对类型T的引用,尝试创建类型”lvalue引用cv TR“将创建类型”lvalue引用T“,而尝试创建类型”rvalue引用cv TR“则会创建类型TR。”

在人类形态中(TR =对T类型的引用,R=引用):

代码语言:javascript
运行
复制
TR      R
T& & -> T&    // an lvalue reference to cv TR (becomes)-> lvalue reference to T 
T& && -> T&   // an rvalue reference to cv TR (becomes)-> TR (lvalue reference to T) 
T&& & -> T&   // an lvalue reference to cv TR (becomes)-> lvalue reference to T 
T&& && -> T&& // an rvalue reference to cv TR (becomes)-> TR (rvalue reference to T)

这里最重要的优点是,现在可以跟踪接收到的函数的类型:您可以接收一个l-值并将相同的l-值传递给E,或者您可以接收一个r值并将相同的r值传递给E(在转换它之后,因为对任何类型引用的l-值引用变成了l-值引用):

代码语言:javascript
运行
复制
template<typename T> void E(T&& a) {}

template<typename T> void F(T&& a) { E(static_cast<T&&>(a)); }

一种句法糖

代码语言:javascript
运行
复制
static_cast<T&&>(a)

代码语言:javascript
运行
复制
std::forward<T>(a); // is the same as static_cast<T&&>(a);

因此,解决问题并使您的生活更轻松的最终代码是

代码语言:javascript
运行
复制
template<typename T> void E(T&& a) {}

template<typename T> void F(T&& a) { E(std::forward<T>(a)); }

实例化

参考资料:赫伯萨特的博客和其他一些来源,不幸的是,我找不到了。如果有人有线索,请写在下面的评论,我会更新这篇文章。谢谢。

票数 16
EN

Stack Overflow用户

发布于 2014-07-14 09:27:04

处理r值引用和基准塌陷可能比最初看上去更加复杂。

完美转发

完美转发是为了确保提供给函数的参数被转发(传递)到与原提供相同的值类别(基本上是r值对l值)的另一个函数。

它通常与可能发生引用折叠的模板函数一起使用。

它也可以是在同一功能中使用

Scott在他的2013年土生土长的介绍中给出了下面的伪代码来解释std::forward的工作原理(大约在20分钟内);

代码语言:javascript
运行
复制
template <typename T>
T&& forward(T&& param) { // T&& here is formulated to disallow type deduction
  if (is_lvalue_reference<T>::value) {
    return param; // return type T&& collapses to T& in this case
  }
  else {
    return move(param);
  }
}

示例

上面的一个例子,一个典型的例子是make_unique

代码语言:javascript
运行
复制
template<class T, class... U>
std::unique_ptr<T> make_unique(U&&... u)
{
    return std::unique_ptr<T>(new T(std::forward<U>(u)...));
}

在示例中,unique_ptr的参数通过make_unique提供给它,就好像它们是直接提供给unique_ptr的一样,即保持参数的引用、l值和r值性质。

一个更具体的例子;

代码语言:javascript
运行
复制
#include <iostream>
#include <utility>
#include <memory>

struct A {
  // implementation excluded
};

struct B {
  B(A &) // ctor 1
  {
    std::cout << "ctor 1" << std::endl;
  }
  B(A&&) // ctor 2
  {
    std::cout << "ctor 2" << std::endl;
  }
};

int main()
{
  A a;
  auto b1 = std::make_unique<B>(a); // ctor 1 is used
  auto b2 = std::make_unique<B>(A()); // ctor 2 is used
}

简略的

完美的转发依赖于一些C++11新的基本语言结构,这些构造构成了我们现在在泛型编程中看到的大部分内容的基础:

  • 基准塌陷
  • Rvalue引用
  • 移动语义

std::forward的使用目前在公式化的std::forward<T>中,了解std::forward的工作原理有助于理解为什么会这样,还有助于识别rvalue的非惯用或错误使用、引用崩溃等。

Thomas在完美的转发问题和解决方案上提供了一个漂亮但密集的写下来

票数 3
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/24732926

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档