不理解左值的双重转发 - 按值传递时
not understanding double forwarding of an lvalue - when passed by value
#include <iostream>
#include <vector>
#include <type_traits>
#include <utility>
using namespace std;
template <typename Func, typename... Args>
void proxy(Func f, Args&&... args) {
f(std::forward<Args>(args)...);
}
void real_func(vector<int> v) {
cout << "size: " << v.size() << endl;
}
void multicast_func(vector<int> v) {
proxy(real_func, std::forward<vector<int>>(v));
proxy(real_func, std::forward<vector<int>>(v));
}
int main()
{
vector<int> ints = {1, 2, 3};
multicast_func(ints);
return 0;
}
输出为:
size: 3
size: 0
为什么不是 3、3?这个左值在什么时候变成右值并被移出?
at what point does this lvalue become an rvalue and gets moved-from?
在第一次调用 proxy
时 v
被转换为右值,然后在传递给 real_func
时被移出。
void multicast_func(vector<int> v) {
// the return type of std::forward is T&&, i.e. vector<int>&& here
// for functions whose return type is rvalue reference to objec, the return value is an rvalue
// that means v is converted to an rvalue and passed to proxy
proxy(real_func, std::forward<vector<int>>(v));
// v has been moved when passed to real_func as argument
proxy(real_func, std::forward<vector<int>>(v));
}
proxy
中的用法是std::forward
的一般用法;根据参数是左值还是右值,模板参数将推导为T&
或T
。对于 T&
std::forward
将 return 一个左值,对于 T
std::forward
将 return 一个右值,因此值类别被保留。当您单独指定模板参数时,这样的容量就会丢失。
std::forward
旨在与通用引用一起使用。
multicast_func
的参数不是通用引用,所以std::forward
没有意义:
void multicast_func(vector<int> v) {
proxy(real_func, std::forward<vector<int>>(v));
proxy(real_func, std::forward<vector<int>>(v));
}
在这种情况下,它的作用类似于std::move
(因为模板参数不是(左值)引用)。
std::forward
,当没有给定引用类型时,会将提供的对象转换为右值。这意味着第一次调用
proxy(real_func, std::forward<vector<int>>(v));
将使 v
成为右值,这意味着它会将其移至 real_func
。然后第二次调用使用从对象移动的那个,你得到 0
的大小,因为它已被清空。
如果我们看一下函数,这是有道理的。您调用的 std::forward
的版本是
template< class T >
constexpr T&& forward( typename std::remove_reference<T>::type& t ) noexcept;
由于您通过 std::vector<int>
获得 T
,这意味着它将 return 成为 std::vector<int>&&
。因此,即使 v
是左值,它也会被转换为右值。如果你想保持 v
的左值性,那么你需要使用 std::vector<int>&
。这给了你 std::vector<int>& &&
并且引用折叠规则将它变成了 std::vector<int>&
给你留下了一个左值。
您的代码中调用的 std::forward
的原型是:
template< class T >
constexpr T&& forward( typename std::remove_reference<T>::type& t ) noexcept;
当使用非引用类型调用时,它有效地从参数中创建右值引用,然后从中移动。 std::vector
被移出后保证为空,所以size
变为0。
在proxy
的第一次调用中,参数vector<int> v
(在函数real_func
中)是从右值构造的,所以v
(在函数multicast_func
) 已为空。
但是如果你把参数类型改成cosnt vector<int> &
,结果就是3, 3。因为还没有调用move constrctor。
void real_func(const vector<int>& v) {
cout << "size: " << v.size() << endl;// the output is 3, 3
}
#include <iostream>
#include <vector>
#include <type_traits>
#include <utility>
using namespace std;
template <typename Func, typename... Args>
void proxy(Func f, Args&&... args) {
f(std::forward<Args>(args)...);
}
void real_func(vector<int> v) {
cout << "size: " << v.size() << endl;
}
void multicast_func(vector<int> v) {
proxy(real_func, std::forward<vector<int>>(v));
proxy(real_func, std::forward<vector<int>>(v));
}
int main()
{
vector<int> ints = {1, 2, 3};
multicast_func(ints);
return 0;
}
输出为:
size: 3
size: 0
为什么不是 3、3?这个左值在什么时候变成右值并被移出?
at what point does this lvalue become an rvalue and gets moved-from?
在第一次调用 proxy
时 v
被转换为右值,然后在传递给 real_func
时被移出。
void multicast_func(vector<int> v) {
// the return type of std::forward is T&&, i.e. vector<int>&& here
// for functions whose return type is rvalue reference to objec, the return value is an rvalue
// that means v is converted to an rvalue and passed to proxy
proxy(real_func, std::forward<vector<int>>(v));
// v has been moved when passed to real_func as argument
proxy(real_func, std::forward<vector<int>>(v));
}
proxy
中的用法是std::forward
的一般用法;根据参数是左值还是右值,模板参数将推导为T&
或T
。对于 T&
std::forward
将 return 一个左值,对于 T
std::forward
将 return 一个右值,因此值类别被保留。当您单独指定模板参数时,这样的容量就会丢失。
std::forward
旨在与通用引用一起使用。
multicast_func
的参数不是通用引用,所以std::forward
没有意义:
void multicast_func(vector<int> v) {
proxy(real_func, std::forward<vector<int>>(v));
proxy(real_func, std::forward<vector<int>>(v));
}
在这种情况下,它的作用类似于std::move
(因为模板参数不是(左值)引用)。
std::forward
,当没有给定引用类型时,会将提供的对象转换为右值。这意味着第一次调用
proxy(real_func, std::forward<vector<int>>(v));
将使 v
成为右值,这意味着它会将其移至 real_func
。然后第二次调用使用从对象移动的那个,你得到 0
的大小,因为它已被清空。
如果我们看一下函数,这是有道理的。您调用的 std::forward
的版本是
template< class T >
constexpr T&& forward( typename std::remove_reference<T>::type& t ) noexcept;
由于您通过 std::vector<int>
获得 T
,这意味着它将 return 成为 std::vector<int>&&
。因此,即使 v
是左值,它也会被转换为右值。如果你想保持 v
的左值性,那么你需要使用 std::vector<int>&
。这给了你 std::vector<int>& &&
并且引用折叠规则将它变成了 std::vector<int>&
给你留下了一个左值。
您的代码中调用的 std::forward
的原型是:
template< class T >
constexpr T&& forward( typename std::remove_reference<T>::type& t ) noexcept;
当使用非引用类型调用时,它有效地从参数中创建右值引用,然后从中移动。 std::vector
被移出后保证为空,所以size
变为0。
在proxy
的第一次调用中,参数vector<int> v
(在函数real_func
中)是从右值构造的,所以v
(在函数multicast_func
) 已为空。
但是如果你把参数类型改成cosnt vector<int> &
,结果就是3, 3。因为还没有调用move constrctor。
void real_func(const vector<int>& v) {
cout << "size: " << v.size() << endl;// the output is 3, 3
}