我知道关于现有资源的共享所有权的问题之前已经被问过很多次了,但不知何故我没能找到我的具体问题的答案。如果我错了,请纠正我。
我正在使用表达式模板库。这是一个玩具库,我想探索的一件事是通过表达式模板使用 C++ 类型对算法进行编码的优缺点。这可以用于例如惰性评估和算法微分。为此,库的用户应该能够修改现有的代码块,以便它通过运算符重载在后台使用我的库的表达式模板。
现在我不确定如何处理子表达式的所有权。例如,考虑以下函数:
auto Foo() (
auto alpha = [...]; // some calculation, result is an expression, e.g. Div<Sub<Value,Value>,Sub<Value,Value>>
return (1-alpha)*something + alpha*something_else;
)
此函数创建一个新的类型表达式
Add<Mult<Sub<Value,TYPE_OF_ALPHA>,TYPE_OF_SOMETHING>, Mult<TYPE_OF_ALPHA, TYPE_OF_SOMETHING_ELSE>>
似乎很清楚,表示1-alpha
和alpha*something_else
应该共享所有权的表达式alpha
,因为alpha
在我们退出时将超出范围Foo
。这告诉我,我应该shared_ptr
在表达式中使用成员来子表达式。- 或者这已经是一个误解?
我的问题
我将如何编写我的二元运算表达式的构造函数Sub
和Mult
,以便表达式真正占用传递给构造函数的对象/子表达式/操作数的共享所有权 - 这样,用户必须对函数进行的更改Foo
保持不变最小?
- 我不想将对象移动到
1-alpha
中,因为alpha
在我调用alpha*something_else
. - 如果我使用
make_shared
,则两个表达式都将shared_ptr
s 存储到 的副本中alpha
,这并不是真正的共享权限。评估结果表达式意味着每个 alpha 副本都会被评估,从而产生冗余计算。 - 我可以在函数体中创建一个
shared_ptr
to ,并将这个指针按值传递给and的构造函数。但这将是图书馆用户的负担。优选地,实现细节很好地隐藏在运算符重载之后:理想情况下,用户只需对其现有函数进行最小的更改即可将其与表达式模板库一起使用。我要求太多了吗,在这里?alpha
Foo
1-alpha
alpha*something_else
Foo
编辑1:
这是一个示例,我使用第二个选项,在构造函数中创建副本(我认为......):https ://godbolt.org/z/qn4h3q
编辑2:
我找到了一个可行的解决方案,但我不愿意回答我自己的问题,因为我对我的解决方案不是 100% 满意。
如果我们想要共享作为自定义类的资源的所有权,我们可以修改(在我的情况下是这样),我们可以为该类配备一个shared_ptr
自身的副本。shared_ptr
只要没有人获得该nullptr
对象的所有权,就可以。
#include <memory>
#include <iostream>
class Expression
{
public:
Expression(std::string const& n)
: name(n)
{}
~Expression(){
std::cout<<"Destroy "<<name<<std::endl;
}
std::shared_ptr<Expression> shared_copy()
{
return shared_ptr? shared_ptr : shared_ptr = std::make_shared<Expression>(*this);
}
std::string name;
std::shared_ptr<Expression> subexpression;
private:
std::shared_ptr<Expression> shared_ptr = nullptr;
};
int main()
{
// both a and b shall share ownership of the temporary c
Expression a("a");
Expression b("b");
{
Expression c("c");
a.subexpression = c.shared_copy();
b.subexpression = c.shared_copy();
// rename copy of c now shared between a and b
a.subexpression->name = "c shared copy";
}
std::cout<<"a.subexpression->name = "<<a.subexpression->name<<std::endl;
std::cout<<"b.subexpression->name = "<<b.subexpression->name<<std::endl;
std::cout<<"b.subexpression.use_count() = "<<b.subexpression.use_count()<<std::endl;
}
输出:
Destroy c
a.subexpression->name = c shared copy
b.subexpression->name = c shared copy
b.subexpression.use_count() = 2
Destroy b
Destroy a
Destroy c shared copy
实时代码示例:https ://godbolt.org/z/xP45q6 。
这是使用实际表达式模板对编辑 1中给出的代码示例的改编: https ://godbolt.org/z/86YGfe 。
我对此不满意的原因是我担心我真的扼杀了我希望从 ET 获得的任何性能提升。首先,我必须将shared_ptr
s 带入操作数,这已经够糟糕的shared_ptr
了. ET 是轻量级的。