C++ 如何在C++;返回值或引用,复制最少?
我有一个函数,它委托给另外两个函数,根据某些运行时条件返回引用或值:C++ 如何在C++;返回值或引用,复制最少?,c++,c++11,C++,C++11,我有一个函数,它委托给另外两个函数,根据某些运行时条件返回引用或值: X by_value() { ... } const X& by_reference() { ... } ?? foo(bool b) { if (b) { return by_value(); } else { return by_reference(); } } 我想选择我的函数的返回类型,这样调用者可以减少复制;e、 g: const X& x1 = foo(true);
X by_value() { ... }
const X& by_reference() { ... }
?? foo(bool b) {
if (b) {
return by_value();
} else {
return by_reference();
}
}
我想选择我的函数的返回类型,这样调用者可以减少复制;e、 g:
const X& x1 = foo(true); // No copies
const X& x2 = foo(false); // No copies
X x3 = foo(true); // No copies, one move (or zero via RVO)
X x4 = foo(false); // One copy
在除最后一种情况外的所有情况下,都不需要(基于运行时行为)复制返回值
如果foo
的返回类型为X
,则案例2中会有一个额外的副本;但是如果返回类型是常量X&
,那么案例1和3是未定义的行为
是否可以通过返回某种代理来确保上述使用具有最少的副本
解释:由于“你做错了”这一形式遭到了严重的抵制,我想我应该解释一下原因 假设我有一个类型为
T或function
的数组(意思是这个数组的元素要么是T
类型,要么是返回T
的函数)。我所说的数组元素的“值”,是指值本身或函数求值时的返回值
如果这个get\u value\u of_array(int index)
按值返回,那么在数组只包含一个元素的情况下,我必须进行额外的复制。这就是我想要避免的
进一步注意:如果答案是“那是不可能的”,那我没问题。不过,我很想看到这方面的证明——理想的形式是“假设有一个类型
代理解决了您的问题。然后…`您的目标是错误的:有多个返回类型,其中一个是副本,另一个是引用,这会使函数无法预测
假设foo是某些类a、B的成员函数:
制造
明确的
及
悬空引用您要查找的是一个(即,一种类型,其可能值为“可能的X
值加上可能的X常量&
值”)
<>在C++中,这些通常称为代码>变体。这些通常实现为标记加上适当大小和对齐的数组,并且只在运行时保持一个值。或者,它们是用动态分配和经典的< /P>实现的。
例如,使用,可以声明函数返回boost::variant
():
boost::变量foo(bool b){
如果(b){
按_值()返回;
}否则{
通过_reference()返回;
}
}
在我写这个答案的时候,函数foo
的参数不要求在运行时计算,也不要求允许它不是字面上的false
或true
,因此:
#include <utility>
#include <iostream>
namespace my {
using std::cout; using std::endl;
class X
{
private:
X& operator=( X const& ) = delete;
public:
X() {}
X( X const& )
{ cout << "Copy" << endl; }
X( X&& )
{ cout << "Move" << endl; }
};
} // my
auto foo_true()
-> my::X
{ return my::X(); }
auto foo_false()
-> my::X const&
{ static my::X const static_x; return static_x; }
#define foo( arg ) foo_##arg()
auto main() -> int
{
using namespace my;
cout << "A" << endl; const X& x1 = foo(true); // No copies
cout << "B" << endl; const X& x2 = foo(false); // No copies
cout << "C" << endl; X x3 = foo(true); // No copies, one move (or zero via RVO)
cout << "D" << endl; X x4 = foo(false); // One copy
}
#包括
#包括
名称空间我的{
使用std::cout;使用std::endl;
X类
{
私人:
X&运算符=(X常量&)=删除;
公众:
X(){}
X(X常数&)
{cout我认为这是不可能的,因为调用方是否决定移动或复制返回值(无论是从代理还是从您的类本身)是一个编译时决定,而您想要的是使其成为一个运行时决定。重载解析不能在运行时发生
我能看到的唯一解决办法是让被叫方决定这一点,即提供一个T&
参数,它可以根据自己认为合适的内容移动或复制分配到该参数。
或者,如果您认为调用者不可能创建某种“null”实例,您可以传递一个对齐的\u存储
缓冲区,让被调用者在缓冲区中构造值。如果您真的想实现这一点,这里有一个相当丑陋的方法:
X *foo(bool b, X *value) {
if (b) {
*value = get_value();
} else {
value = get_pointer_to_value();
}
return value;
}
用法示例:
void examplefunc() {
X local_store;
X *result;
result = foo(true, &local_store);
assert(result == &local_store);
use_x_value(*x);
result = foo(false, &local_store);
assert(result != &local_store);
use_x_value(*x);
}
上述方法很麻烦:它需要两个局部变量,并强制通过指针使用返回值。它还公开了一个原始指针,无法很好地转换为智能指针(将local\u-store
放入堆中以允许使用智能指针将使此方法更加复杂,更不用说增加堆分配的开销)。此外,local\u-store
始终是默认构造的,但如果不需要使examplefunc
重新进入,则可以将其设置为静态
(或对多线程版本使用线程本地存储)
因此,我很难想象你到底想在哪里使用它。如果总是只返回一个复制的值(并在可能的情况下让编译器处理复制省略),或者总是返回一个引用,或者总是返回一个共享的\u ptr
,那就更简单了。你可能能够完成你想要的(仍然有点模糊)通过将变量类型传递给数组的获取值。\u
。这将允许它返回两种不同的类型,并根据数组的成员是函数还是数组进行调整
struct X
{
X() { std::cout << "Construct" << std::endl; }
X(X const&) { std::cout << "Copy" << std::endl; }
X(X&&) { std::cout << "Move" << std::endl; }
};
const X array;
X function() { return X(); }
template<typename ReturnType>
ReturnType get_value_of_array(bool);
template<>
const X& get_value_of_array<const X&>(bool /*isarray*/)
{
// if (isarray == false) return the cached result of function()
return array; // gotta build the example yo!
}
template<>
X get_value_of_array<X>(bool isarray)
{
return isarray ? array : std::move(function());
}
int main()
{
// Optimizations may vary.
const X& x1 = get_value_of_array<decltype(x1)>(true); // No copies or moves
const X& x2 = get_value_of_array<decltype(x2)>(false); // No copies or moves.
X x3 = get_value_of_array<decltype(x3)>(true); // One copy, one move.
X x4 = get_value_of_array<decltype(x4)>(false); // Two moves.
}
struct X
{
X(){std::难道你的编译器不提供复制省略吗?我觉得仅仅为了避免复制而破坏按值返回和按引用返回语义是个坏主意。请相信RVO/elision来完成它的工作,当你真的想返回引用时返回引用,当你真的想返回值时返回值。Implicit=bad,explicit=good。通过更明确的方式使所有问题消失。简单。{提示:您确实有两个函数,您已将它们与运行时的选择合并为一个}返回一个variant
@JesseBeder:通常,变量是通过包含所需类型的最大对齐和大小的char
数组来实现的,并将相关值构造到该数组中。不会有默认构造。我理解通过引用返回可能导致挂起引用;如果我我不想留下一份悬而未决的推荐信,你能回答这个问题吗?就因为你答应了
X *foo(bool b, X *value) {
if (b) {
*value = get_value();
} else {
value = get_pointer_to_value();
}
return value;
}
void examplefunc() {
X local_store;
X *result;
result = foo(true, &local_store);
assert(result == &local_store);
use_x_value(*x);
result = foo(false, &local_store);
assert(result != &local_store);
use_x_value(*x);
}
struct X
{
X() { std::cout << "Construct" << std::endl; }
X(X const&) { std::cout << "Copy" << std::endl; }
X(X&&) { std::cout << "Move" << std::endl; }
};
const X array;
X function() { return X(); }
template<typename ReturnType>
ReturnType get_value_of_array(bool);
template<>
const X& get_value_of_array<const X&>(bool /*isarray*/)
{
// if (isarray == false) return the cached result of function()
return array; // gotta build the example yo!
}
template<>
X get_value_of_array<X>(bool isarray)
{
return isarray ? array : std::move(function());
}
int main()
{
// Optimizations may vary.
const X& x1 = get_value_of_array<decltype(x1)>(true); // No copies or moves
const X& x2 = get_value_of_array<decltype(x2)>(false); // No copies or moves.
X x3 = get_value_of_array<decltype(x3)>(true); // One copy, one move.
X x4 = get_value_of_array<decltype(x4)>(false); // Two moves.
}