【问题标题】:Why not always use std::forward?为什么不总是使用 std::forward?
【发布时间】:2016-03-25 22:05:01
【问题描述】:

std::movestd::forward 之间的区别众所周知,我们使用后者来保留转发对象的值类别,而前者用于强制转换为右值引用以启用移动语义。

有效的现代 C++ 中,存在一条说明

在右值引用上使用std::move,在通用引用上使用std::forward

但在以下场景中(以及我们不想更改值类别的场景),

template <class T>
void f(vector<T>&& a)
{
    some_func(std::move(a)); 
}

其中a不是转发引用而是简单的右值引用,那么做下面不就完全一样了吗?

template <class T>
void f(vector<T>&& a)
{
    some_func(std::forward<decltype(a)>(a)); 
}

由于这可以很容易地封装在这样的宏中,

#define FWD(arg) std::forward<decltype(arg)>(arg)

总是这样使用这个宏定义不是很方便吗?

void f(vector<T>&& a)
{
    some_func(FWD(a)); 
}

这两种写法不是完全等价的吗?

【问题讨论】:

  • 我无法将宏解决方案视为比写 move()... 更方便...但这让我觉得这是一个基于意见的问题。两者都做同样的事情
  • 但有时参数是常规引用,您确实想移动向量。继续使用转发并不能做到这一点
  • @Barry 我想确认它们是等价的,谢谢。我不同意他们是否基于意见,仅仅倡导一种编程风格并不意味着偏见,一个人可能有很好的理由这样做(并且能够详细说明)
  • 添加宏会混淆理解。如果你故意做一些奇怪的事情,就像你在这里做的那样,它应该是显而易见的,即使是以冗长为代价。
  • 关于“……做这个会不会完全一样……”我觉得完全一样;因为 vector&& a 是一个右值引用,并且对右值引用进行无条件强制转换(移动)或有条件强制转换(转发)没有区别。

标签: c++ c++11


【解决方案1】:

嗯。值得商榷。在您的特定示例中,它是等效的。但我不会让它成为一种习惯。一个原因是因为您想要在转发和移动之间进行语义区分。另一个原因是因为要拥有一致的 API,除了 FWD 之外,您还必须拥有 MOV,这看起来真的很糟糕,而且没有任何作用。不过,更重要的是,您的代码可能会意外失败。

考虑以下代码:

#include <iostream>

using namespace std;

#define FWD(arg) std::forward<decltype(arg)>(arg)

struct S {
    S() { cout << "S()\n"; }
    S(const S&) { cout << "S(const S&)\n"; }
    S(S&&) { cout << "S(S&&)\n"; }
};

void some_func(S) {}

void f(S&& s)
{
    some_func(FWD(s)); 
}

int main()
{
    f(S{});
}

打印出来

S()
S(S&&)

但是,如果我只是将 FWD 行更改为具有另一对(看似可选的)括号,如下所示:

void f(S&& s)
{
    some_func(FWD((s))); 
}

现在我们得到

S()
S(const S&)

这是因为现在我们在一个表达式上使用decltype,它的计算结果是一个左值引用。

【讨论】:

  • 你说得很好。但我不同意括号的“看似可选”的特征。如果使用decltype 应该知道this stuff,即使在std::forward&lt;decltype...(这是通用lambda 中经常遇到的语法)中也可能出现这种错误,而不是宏
  • @Lorah 是的,但它是一个宏——你不能立即看到它使用了decltype
  • @Barry 我猜如果它是一个函数,你也看不到它的作用,你总是必须阅读代码;我主要将与宏相关的“缺乏透明度”理解为“奇怪的副作用或行为突变”,即使您阅读代码也不明显(例如两次评估表达式等......)。出于FWD 之类的原因,我不喜欢使用宏,即文本替换(没有双重评估,没有附带的数据结构,没有隐藏的控制流)。你能看看this吗? (这是题外话,但我可以使用一些帮助)
  • @LorahAttkins 这里的部分问题是首先使用 decltype。它并不是真正需要的,它带来了它的微妙之处。
  • @LorahAttkins 我同意你的看法。您提出的带有 std::forward 的宏确实有效(与之前的任何东西一样)。 @YamMarcovic 所说的无论如何都应该是已知的。你可以使用std::forward 来处理几乎所有事情,除非你特别想要一个右值转换,在这种情况下你可以使用std::move
猜你喜欢
  • 2019-04-14
  • 2014-12-20
  • 2017-11-05
  • 2012-10-24
  • 2016-11-10
  • 2014-06-12
  • 2022-01-09
  • 2016-11-15
  • 2011-07-03
相关资源
最近更新 更多