c ++:对转发引用的困惑
c++: confusion about forwarding reference
我阅读了 this(写得非常好)关于 Forwarding Reference in C++11 的文章,作者是 Scott Meyers。
现在,重点关注文章的这一部分:
template <class... Args>
void emplace_back(Args&&... args); // deduced parameter types ⇒ type deduction;
... // && ≡ universal references
因此,与其他情况相比,省略号不会使 &&
成为右值引用,但它仍然是通用引用。
据我了解,当我们拥有通用引用时,我们可以调用传递右值和左值的函数(哇,太酷了!)
现在,我实现了这个功能:
template <typename ReturnType, typename... Args>
ReturnType callFunction(MemFunc<ReturnType, Args...> memFunc, Args&& ... args) { ...
所以(使用与前面示例相同的逻辑),&&
表示转发引用。
但是如果我尝试拨打这个电话:
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);
}
所以首先,请使用 "forwarding reference" 而不是 "universal reference"。它更好地代表了它是什么以及它的预期用途。
首先要注意的是,并非每个 &&
都是转发引用。它也可以是右值引用。
简单来说,T&&
是转发参考当且仅当:
T
是 simple(简单如下所示)类型(例如 vector<int>&&
或 vector<T>&&
是 不是转发参考)。
- 和
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&
并且通过折叠规则:X& &&
变为 X&
,因此我们有一个左值引用。如您所料。
在第二个中,T
将被推导为 X
并且通过折叠规则 X &&
变为 X&&
,因此我们有一个右值引用。
但是当你这样称呼它时:
foo<X>(x);
T
不再推导。您基本上是说让 T
成为 X
。因此,如果 T
是 X
,则 T &&
是 X&&
,并且您会遇到错误: p
whose type is now X&&
cannot bind to an左值。
霍尔特还补充道:
Also note that because of the declaration of sortFunc, this would not
work even if you did not specify the function template arguments
explicitely.
我倾向于同意他的观点,但我需要进一步调查才能确定这一点。
我阅读了 this(写得非常好)关于 Forwarding Reference in C++11 的文章,作者是 Scott Meyers。
现在,重点关注文章的这一部分:
template <class... Args> void emplace_back(Args&&... args); // deduced parameter types ⇒ type deduction; ... // && ≡ universal references
因此,与其他情况相比,省略号不会使 &&
成为右值引用,但它仍然是通用引用。
据我了解,当我们拥有通用引用时,我们可以调用传递右值和左值的函数(哇,太酷了!)
现在,我实现了这个功能:
template <typename ReturnType, typename... Args>
ReturnType callFunction(MemFunc<ReturnType, Args...> memFunc, Args&& ... args) { ...
所以(使用与前面示例相同的逻辑),&&
表示转发引用。
但是如果我尝试拨打这个电话:
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);
}
所以首先,请使用 "forwarding reference" 而不是 "universal reference"。它更好地代表了它是什么以及它的预期用途。
首先要注意的是,并非每个 &&
都是转发引用。它也可以是右值引用。
简单来说,T&&
是转发参考当且仅当:
T
是 simple(简单如下所示)类型(例如vector<int>&&
或vector<T>&&
是 不是转发参考)。- 和
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&
并且通过折叠规则:X& &&
变为 X&
,因此我们有一个左值引用。如您所料。
在第二个中,T
将被推导为 X
并且通过折叠规则 X &&
变为 X&&
,因此我们有一个右值引用。
但是当你这样称呼它时:
foo<X>(x);
T
不再推导。您基本上是说让 T
成为 X
。因此,如果 T
是 X
,则 T &&
是 X&&
,并且您会遇到错误: p
whose type is now X&&
cannot bind to an左值。
霍尔特还补充道:
Also note that because of the declaration of sortFunc, this would not work even if you did not specify the function template arguments explicitely.
我倾向于同意他的观点,但我需要进一步调查才能确定这一点。