【问题标题】:c++: confusion about forwarding referencec++:关于转发引用的混淆
【发布时间】:2016-08-18 22:41:32
【问题描述】:

我阅读了 this(写得非常好)关于由 Scott Meyers 在 C++11 中转发参考的文章。

现在,关注本文的这一部分:

template <class... Args>
void emplace_back(Args&&... args); // deduced parameter types ⇒ type deduction;
...                                // && ≡ universal references

因此,与其他情况相比,省略号不会使 &amp;&amp; 成为右值引用,但它仍然是通用引用。

据我了解,当我们有通用引用时,我们可以调用传递右值和左值的函数(哇,太酷了!)

现在,我已经实现了这个功能:

template <typename ReturnType, typename... Args>
ReturnType callFunction(MemFunc<ReturnType, Args...> memFunc, Args&& ... args) { ...

所以(使用与上例相同的逻辑),&amp;&amp; 表示转发引用。

但是如果我尝试拨打这个电话:

typedef vector<double> vecD;
vecD vec;
mem.callFunction<vecD, vecD>(sortFunc, vec);

编译器会抱怨You cannot bind an lvalue to an rvalue reference

为什么会这样?

完整代码:

#include <functional>
#include <vector>

using namespace std;
struct MultiMemoizator {
    template <typename ReturnType, typename... Args>
    ReturnType callFunction(std::function<ReturnType(Args...)> memFunc, Args&&... args) {

    }
};

typedef vector<double> vecD;

vecD sort_vec (vecD const& vec) {
    return vec;
}

int main()
{
    vecD vec;
    std::function<vecD(vecD)> sortFunc(sort_vec);
    MultiMemoizator mem;
    mem.callFunction<vecD, vecD>(sortFunc, vec);
}

【问题讨论】:

  • Universal 引用从一开始就是一个糟糕的术语,现在它终于被更合适的 forwarding 引用所取代。
  • 你确定Args实际上是在你的通话中推导出来的吗?
  • 请提供minimal reproducible example。提供的代码不完整(sort_vec 是什么?),而且肯定不是最小的。
  • @bolov 就好像他们只是想查看相关的代码!
  • @justHelloWorld 每个人都是对的:1.您的 sn-p 中缺少一些部分,2.您的代码包含许多与您的问题无关的内容,3.这些与您的问题完全无关, 4. 代码可以是最少的并且可以为编译器工作。 This 可能是您的问题的一个很好的 MCVE,它有 25 行长。

标签: c++ c++11 move move-semantics forwarding-reference


【解决方案1】:

所以首先,请使用“转发参考”而不是“通用参考”。它更好地代表了它是什么以及它的预期用途。

首先要注意的是,并非每个&amp;&amp; 都是转发引用。它也可以是右值引用。

简单来说,T&amp;&amp; 是转发引用当且仅当:

  • T 是一个simple(简单如下图所示)类型(例如vector&lt;int&gt;&amp;&amp;vector&lt;T&gt;&amp;&amp;不是转发引用)。
  • 推导出T

在您的示例中,Args 没有被推断出来。那是因为你在调用时明确指定了函数模板参数Args

mem.callFunction<vecD, vecD>(sortFunc, vec);
                       ^~~~

让我们用更简单的东西来更好地理解:

让我们设置场景:

struct X {};

template <class T>
auto foo(T&& p) {}

在接下来的 2 次调用中,我们有转发引用:

X x;
foo(x);

foo(X{});

首先,T 将被推导出为 X&amp; 并通过折叠规则:X&amp; &amp;&amp; 变为 X&amp;,因此我们有一个左值引用。如您所料。

在第二种情况下,T 将被推导出为X,通过折叠规则X &amp;&amp; 变为X&amp;&amp;,因此我们有一个右值引用。

但是当你这样称呼它时:

foo<X>(x);

T 不再推导。你基本上说让T 成为X。所以如果TX 那么T &amp;&amp;X&amp;&amp;,你就会遇到错误:p 现在的类型是X&amp;&amp; 不能绑定到左值。


霍尔特还补充道:

还要注意,由于 sortFunc 的声明,这不会 即使您没有指定函数模板参数也可以工作 明确地。

我倾向于同意他的观点,但我需要进一步调查才能确定这一点。

【讨论】:

  • 另请注意,由于sortFunc 的声明,即使您没有明确指定函数模板参数,这也不起作用。
  • 我想我理解了你的回答,但我的下一个问题是:有一种方法可以让我的代码“转发可引用”(因此推断出Args 类型)?
  • @justHelloWorld 乍一看我会说不(至少在没有一些讨厌的丑陋黑客的情况下不会)。您需要为sortFunc 指定Args。也许如果您将memFunc 设为最后一个参数(不知道是否可能),那么Args 的扣除首先发生,但随后它会更改memFunc 的签名。但是您可以将remove_reference_t 添加到memFuc 参数的签名中。正如我所说,丑陋丑陋的黑客。
  • @justHelloWorld 也许如果你把它作为一个单独的问题发布(但请尽量减少)有人会想出一个好的解决方案。
  • 在这个话题上打开了这个问题:stackoverflow.com/questions/36842291/…
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-04-11
  • 2014-01-10
  • 2014-12-31
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-17
相关资源
最近更新 更多