我有一个委托给另外两个的函数,根据某些运行时条件返回引用或值:
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中会有一个额外的副本;但如果返回类型为const X&
,则情况1和3是未定义的行为。
是否可以通过返回某种代理来确保上述用途具有最少的副本?
解释:由于表格的显着推迟,你做错了#34;我想我会解释原因对此。
想象一下,我有一个T or function<T()>
类型的数组(意味着此数组的元素属于T
类型,或者它们的函数返回T
)。通过&#34;值&#34;这个数组的元素,我的意思是,值本身或评估函数时的返回值。
如果这个get_value_of_array(int index)
按值返回,那么在数组只包含一个元素的情况下,我被迫做一个额外的副本。这就是我试图避免的。
进一步说明:如果答案是,&#34;那是不可能的&#34;,那对我很好。我很乐意看到这方面的证据 - 理想情况下的形式&#34;假设有Proxy<X>
类型解决了你的问题。然后...`
答案 0 :(得分:6)
您要找的是sum-type(即,可能的值为“可能的X
值加上可能的X const&
值的类型“)。
在C ++中,这些通常称为variant
。这些通常实现为标记加上适当大小和对齐的数组,并且在运行时仅保留一个值。或者,它们通过动态分配和经典visitor pattern实现。
例如,使用Boost.Variant,您可以声明函数返回boost::variant<X, X const&>
(live example):
boost::variant<X, X const&> foo(bool b) {
if (b) {
return by_value();
} else {
return by_reference();
}
}
答案 1 :(得分:1)
我认为这是不可能的,因为调用者是否决定移动或复制返回值(无论是来自代理还是来自您自己的类)是编译时决定,而您想要的是制作这是一个运行时的决定。在运行时不能发生过载分辨率。
我能看到的唯一出路就是让被调用者决定这一点,即提供一个T &
参数,它可以移动 - 分配或复制 - 分配给依赖在它认为合适的地方
或者,您可以传递一个aligned_storage<sizeof(T)>
缓冲区并让被调用者在其中构造值,如果您不认为调用者可以生成一个&#34; null&#34;某种实例。
答案 2 :(得分:1)
好吧,如果真的希望实现这一点,那么这是一个相当丑陋的方式:
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_store
放到堆中以允许使用智能指针会使这种方法更复杂,更不用说添加开销了堆分配)。另外,local_store
始终是默认构造的,但如果您不需要重新examplefunc
,则可以将其static
(或使用线程本地存储多线程版本。)
所以我很难想象你真正想要用它的地方。总是只返回一个复制的值(让编译器尽可能地处理复制省略),或者总是返回一个引用,或者总是返回一个shared_ptr
,这样会更简单。
答案 3 :(得分:0)
你的目标很糟糕:有多个返回类型,其中一个是副本,另一个是引用,这使得函数无法预测。
假设foo是某些类A,B的成员函数:
品牌
X A::foo() { return X(); }
X foo a = A().foo()
定义明确
和
const X& B::foo() { return some_internal_x; }
const X& b = B().foo()
悬空参考
答案 4 :(得分:0)
您可以通过将变量类型传递给get_value_of_array
来完成您想要的任务(仍然有点模糊)。这将允许它返回两种不同的类型,并根据数组的成员是函数还是数组进行调整。
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.
}
感谢Cheers and hth. - Alf实施X。
答案 5 :(得分:-2)
在我写这个答案的时候,并不要求函数foo
的参数应该在运行时计算,或者应该被允许不是字面false
或{ {1}},因此:
true