C++ std::forward是否有使用prvalue的用例?
C++ std::forward是否有使用prvalue的用例?,c++,c++11,move-semantics,rvalue-reference,C++,C++11,Move Semantics,Rvalue Reference,std::forward最常见的用法是完美地转发转发(通用)引用,如 template<typename T> void f(T&& param) { g(std::forward<T>(param)); // perfect forward to g } 有人能告诉我为什么rvalue过载吗?我看不到任何用例。如果要将右值传递给函数,可以按原样传递,无需对其应用std::forward 这与std::move不同,在这里我明白了为什么人们还需要
std::forward
最常见的用法是完美地转发转发(通用)引用,如
template<typename T>
void f(T&& param)
{
g(std::forward<T>(param)); // perfect forward to g
}
有人能告诉我为什么rvalue
过载吗?我看不到任何用例。如果要将右值传递给函数,可以按原样传递,无需对其应用std::forward
这与std::move
不同,在这里我明白了为什么人们还需要rvalue
重载:您可能需要处理不知道要传递什么并且希望无条件支持移动语义的通用代码,例如
编辑为了澄清这个问题,我想问一下为什么必要,以及它的一个用例。这个答案是为了回答@vsoftco的评论
@DarioOO谢谢你的链接。你能写一个简洁的答案吗?从您的示例中,我仍然不清楚为什么还需要为右值定义std::forward 简而言之: 因为没有右值专门化,下面的代码将无法编译
#include <utility>
#include <vector>
using namespace std;
class Library
{
vector<int> b;
public:
// hi! only rvalue here :)
Library( vector<int>&& a):b(std::move(a)){
}
};
int main()
{
vector<int> v;
v.push_back(1);
A a( forward<vector<int>>(v));
return 0;
}
如果你移动那个元素
vector<int> v;
v.push_back(1);
A a( std::move(v)); //what happens here? just moved
std::cout<<v[0]; // OUCH! out of bounds exception
向量v;
v、 推回(1);
A(std::move(v))//这里发生了什么?刚搬家
std::cout我以前一直盯着这个问题,读过霍华德·希南特的链接,经过一个小时的思考,我无法完全理解它。现在我正在寻找答案,五分钟内就得到了答案。(编辑:得到的答案太慷慨了,因为Hinnant的链接有答案。我的意思是我理解了,并且能够用更简单的方式解释,希望有人会觉得有用) 基本上,这允许您根据传入的类型在某些情况下使用泛型。考虑这个代码:
#include <utility>
#include <vector>
#include <iostream>
using namespace std;
class GoodBye
{
double b;
public:
GoodBye( double&& a):b(std::move(a)){ std::cerr << "move"; }
GoodBye( const double& a):b(a){ std::cerr << "copy"; }
};
struct Hello {
double m_x;
double & get() { return m_x; }
};
int main()
{
Hello h;
GoodBye a(std::forward<double>(std::move(h).get()));
return 0;
}
假设在main
中的代码中,h
是Hello2的一个实例。现在,我们不再需要std::forward,因为对std::move(h).get()的调用将返回一个右值。但是,假设代码是泛型的:
template <class T>
void func(T && h) {
GoodBye a(std::forward<double>(std::forward<T>(h).get()));
}
模板
无效函数(T&h){
再见a(std::forward(std::forward(h.get()));
}
现在,当我们调用func
时,我们希望它能够与Hello
和Hello2
一起正常工作,即我们希望触发移动。如果包含外部的std::forward
,则只有当右值为Hello
时才会发生这种情况,因此我们需要它。但是我们到了关键点。当我们将Hello2
的右值传递给此函数时,get()的右值重载将已经返回右值双精度,因此std::forward
实际上正在接受右值。因此,如果它没有,您将无法编写上述完全通用的代码
该死。好吧,既然@vsoftco要求简洁的用例,这里有一个精练的版本(使用他的“my_forward”的想法来实际查看调用的重载)
我通过提供一个没有prvalue的代码示例来解释“用例”,该示例不会编译或表现出不同的行为(不管这是否真的有用)
我们有std::forward
#include <iostream>
template <class T>
inline T&& my_forward(typename std::remove_reference<T>::type& t) noexcept
{
std::cout<<"overload 1"<<std::endl;
return static_cast<T&&>(t);
}
template <class T>
inline T&& my_forward(typename std::remove_reference<T>::type&& t) noexcept
{
std::cout<<"overload 2"<<std::endl;
static_assert(!std::is_lvalue_reference<T>::value,
"Can not forward an rvalue as an lvalue.");
return static_cast<T&&>(t);
}
你会得到:
编译错误
编译
编撰
编译
如您所见,如果只使用两个forward
重载中的一个,则基本上不会编译4个案例中的2个,而如果根本不使用forward
,则只会编译4个案例中的3个。存在将右值作为右值转发的用例。请参阅。这是为了防止右值作为左值转发,而不使用一组额外的enable_if条件吗?(见案例C)@wakjah如何将右值作为左值转发?如果调用f(prvalue)
,其中prvalue
类似于get\u value()
,则传递一个rvalue
,无需转发它。也许我没有明白你的意思。案例B是答案。@DarioOO谢谢你的链接。你能写一个简洁的答案吗?从您的示例中,我仍然不清楚为什么还需要为右值定义std::forward
。谢谢,顺便说一句,您应该拥有库
,而不是A
。我会想一想你的例子,特别是关于向前复制然后移动。好了,forward
只是使用引用折叠规则进行强制转换。但无论如何,再次感谢,我会很快让你知道我的想法。好的,看这里:。我使用名为my_forward
的实现更改了forward
,该实现仅适用于左值。您的代码仍然可以编译。但是,如果您查看,您会看到有两个重载:(1)和(2)。(2) 获取一个pr值。我的问题是为什么(2)是必要的,什么是用例?如您所见,您的代码使用仅为左值定义的my_forward
。我问的有意义吗?在你的例子中,forward(v)
与std::move(v)
相同(请参见声明和引用折叠规则)v
本身仍然是一个左值(有一个名称)。事实上,我的代码使用了我想显示的第一个重载,你指的是另一个重载。这是analoug,但是对于第二个重载,您接受显式移动的内容(如果您删除第一个重载,那么代码将不会编译,除非您使用std::move显式移动,删除引用使仅使用第二个重载无法隐式移动)请看这里,如果答案是你的工作,为什么你的代码与我的答案相似?(更不用说你在我的回答中加入了
之类的内容,但你没有使用它:/moves非常喜欢文案粘贴和编辑,所以为什么不直接编辑呢?)。除了表面上的问题,这一点并不相似。我最初复制并粘贴了您的代码到我的ide中,因为我很好奇。然后我找到了答案,修改了我已经在那里的代码。我没有编辑你的答案,因为它不正确,它不会是一个小修改
template< typename Impl, typename... SmartPointers>
static std::shared_ptr<void>
instancesFactoryFunction( priv::Context * ctx){
return std::static_pointer_cast<void>( std::make_shared<Impl>(
std::forward< typename SmartPointers::pointerType>(
SmartPointers::resolve(ctx))...
) );
}
#include <utility>
#include <vector>
#include <iostream>
using namespace std;
class GoodBye
{
double b;
public:
GoodBye( double&& a):b(std::move(a)){ std::cerr << "move"; }
GoodBye( const double& a):b(a){ std::cerr << "copy"; }
};
struct Hello {
double m_x;
double & get() { return m_x; }
};
int main()
{
Hello h;
GoodBye a(std::forward<double>(std::move(h).get()));
return 0;
}
struct Hello2 {
double m_x;
double & get() & { return m_x; }
double && get() && { return std::move(m_x); }
};
template <class T>
void func(T && h) {
GoodBye a(std::forward<double>(std::forward<T>(h).get()));
}
#include <iostream>
template <class T>
inline T&& my_forward(typename std::remove_reference<T>::type& t) noexcept
{
std::cout<<"overload 1"<<std::endl;
return static_cast<T&&>(t);
}
template <class T>
inline T&& my_forward(typename std::remove_reference<T>::type&& t) noexcept
{
std::cout<<"overload 2"<<std::endl;
static_assert(!std::is_lvalue_reference<T>::value,
"Can not forward an rvalue as an lvalue.");
return static_cast<T&&>(t);
}
#include <vector>
using namespace std;
class Library
{
vector<int> b;
public:
// &&
Library( vector<int>&& a):b(std::move(a)){
}
};
int main()
{
vector<int> v;
v.push_back(1);
Library a( my_forward<vector<int>>(v)); // &
return 0;
}
#include <vector>
using namespace std;
class Library
{
vector<int> b;
public:
// &&
Library( vector<int>&& a):b(std::move(a)){
}
};
int main()
{
vector<int> v;
v.push_back(1);
Library a( my_forward<vector<int>>(std::move(v))); //&&
return 0;
}
#include <vector>
using namespace std;
class Library
{
vector<int> b;
public:
// &
Library( vector<int> a):b(a){
}
};
int main()
{
vector<int> v;
v.push_back(1);
Library a( my_forward<vector<int>>(v)); // &
return 0;
}
#include <vector>
using namespace std;
class Library
{
vector<int> b;
public:
// &
Library( vector<int> a):b(a){
}
};
int main()
{
vector<int> v;
v.push_back(1);
Library a( my_forward<vector<int>>(std::move(v))); //&&
return 0;
}
Library a( std::move(v));
//and
Library a( v);