在许多情况下,我希望创建一个新的数据实例,并将其返回给API调用者。
我了解到unique_ptr/shared_ptr可以用于工厂模式(例如,Factory pattern using unique_ptr in c++)
同时,我了解到在许多编译器(例如,Efficient way to return a std::vector in c++)中返回值优化(RVO)是可能的。
我更喜欢RVO,因为不用包装unique_ptr就可以更容易地使用返回的值,并且更容易阅读代码,但是,由于RVO没有得到保证,我不想意外地牺牲性能,必须使用unique_ptr来确保返回的值是moved而不是复制的。
是否有任何方法可以显式地指定要移动的返回值,这样如果RVO是可能的,它就不会抱怨什么,或者如果RVO不可能,它会触发一些编译器警告?如果这是可能的话,在这种情况下,我可以安全地摆脱返回unique_ptr。
我使用的是C++17,需要在macOS上支持AppleClang11.0,在Linux上支持g++ 9。
编辑:
我仍然在学习C++,并且在发布这个问题时没有区分RVO (返回值优化)和NRVO (称为返回值优化)。在我看来,NRVO在类似工厂方法这样的模式中更为常见和有用,例如:
vector<foo> vec;
// populate data into vec
return vec;我正在寻找类似于return std::move_only(returned_value)的东西,如果这个值不能被移动(而不是复制到移动),它会给我一个编译器警告。也许我应该重新表述我的问题:如果NRVO没有得到保证,为什么“按值返回”仍然是这个问题(Efficient way to return a std::vector in c++)中推荐的方法,答案不应该是“它取决于”您的函数实现,以及您是否可以接受意外的性能成本?
发布于 2020-03-05 13:44:52
如何确保执行RVO而不是复制?
该语言已经为您从C++17开始执行此操作。
T foo() { /*stuff*/; return T{ /*stuff*/ }; }然后,由于guaranteed copy elision,返回的对象肯定会被省略。
如果你有一个类似的构造
T foo()
{
T obj{ /*stuff*/ };
// do stuff with obj
return obj;
}然后,您将得到NRVO (Nammed返回值优化),这是不可靠的,或者编译器将移动obj,因为在标准中有一条规则,如果所有具有自动存储持续时间的函数本地对象都将被移出函数之外,如果它们有一个移动构造函数。
这意味着,只有当您返回一个无法优化的对象(它是一个命名的本地参数或函数参数)时,和才会得到它不支持移动的副本。全局对象总是被复制,因为它们没有作用域到函数。
发布于 2020-03-05 13:31:15
我更喜欢RVO,因为不用包装unique_ptr更容易使用返回的值
在不可能使用RVO、NRVO或隐式移动的情况下,您不能返回unique_ptr。它是不可复制的:
std::unique_ptr<int> ptr1;
std::unique_ptr<int> ptr2;
ptr2 = ptr1; // error: not copyable这不编译。如果不是RVO、NRVO或move,这也不会编译:
std::unique_ptr<int> foo()
{
return std::unique_ptr<int>{};
}在这种情况下,这是由于C++17的保证RVO。但是即使没有RVO,你也会得到一个移动而不是一个副本。
如果不是因为NRVO或保证移动退步,这就不会编译:
std::unique_ptr<int> foo()
{
std::unique_ptr<int> ptr;
return ptr;
}因此,您已经依赖于RVO,NRVO或移动。不需要unique_ptr。如果您的类型是可移动的,则可以确保即使在NRVO不可能的情况下也不会执行副本,例如并非所有return语句都返回相同的本地对象:
std::unique_ptr<int> foo(const bool flag)
{
if (flag) {
std::unique_ptr<int> ptr1;
return ptr; // implicit move
}
std::unique_ptr<int> ptr2;
return ptr2; // implicit move
}https://stackoverflow.com/questions/60546268
复制相似问题