Ema*_*man 16 c++ shared-ptr c++11 c++14
几个小时前我问了一个关于连接矢量的两个元素的类似问题.现在,我想让我的问题更加笼统.我们假设我们有两个double类型的对象double d1, d2.我们希望第三个object(double d3)获取值d1+d2,这样如果我们改变d1或者d2,则d3自动获得新值d1+d2.我们怎样才能用C++做到这一点?
这就是我的意思:
int main(){
double d1,d2,d3;
d1=4;
d2=7;
//some operations to make d3=d1+d2
std::cout<<d3<<endl;// I want it to print 11
d2=-4;
std::cout<<d3<<endl;//Now without any further operations between these line, it should print 0
return 0;
}
Run Code Online (Sandbox Code Playgroud)
谢谢.
Jar*_*d42 11
您可以创建一个包装器,作为lambda:
double d1 = 0, d2 = 0;
auto l = [&](){ return d1 + d2; };
l(); // 0
d1 = 40;
d2 = 2;
l(); // 42
Run Code Online (Sandbox Code Playgroud)
如果您希望所有变量具有相同的类型,您可以添加类型擦除包装器std::function:
std::function<double()> f1 = [] { return 0; };
std::function<double()> f2 = [] { return 0; };
std::function<double()> sum = [&] { return f1() + f2(); };
std::cout << sum() << std::endl; // 0
f1 = [] { return 40; };
f2 = [] { return 2; };
std::cout << sum() << std::endl; // 42
Run Code Online (Sandbox Code Playgroud)
您的问题是参数绑定的经典动机.
#include <iostream>
#include <functional>
//generic add
template <typename T>
void Add(T x, T y, T & z){
z = x + y;
}
int main(){
//z will change automatically in function call
double z = 0;
//bind z as the result
using namespace std::placeholders;
auto add = std::bind(Add<double>, _1, _2, std::ref(z));
//z is implicity passed and changed
add(6,4);
//outputs 10
std::cout << z << '\n';
}
Run Code Online (Sandbox Code Playgroud)
bind和reference wrappers可以帮助您实现所需的功能.
编写一个包装器,它将存储指向doubles的指针(如原始问题中所推荐的那样).请注意,如果doubles超出范围但counter不会超出范围,这将无效.此外,您可以重载转换为T运算符以摆脱total()功能.
template<typename T>
class counter
{
public:
void regist(T& d)
{
refs.push_back(&d);
}
T total()
{
T total{};
for (auto d : refs)
total += *d;
return total;
}
private:
std::vector<T*> refs;
};
int main(int argc, char* argv[])
{
double d1 = 1.6;
double d2 = 7.2;
double d3 = 1e-4;
counter<double> cnt;
cnt.regist(d1);
cnt.regist(d2);
cnt.regist(d3);
std::cout << cnt.total() << std::endl; // 8.8001
d2 = -7.1;
std::cout << cnt.total() << std::endl; // -5.4999
}
Run Code Online (Sandbox Code Playgroud)
在编译时,不,充其量,你最终会得到一些令人讨厌的模板和宏黑客仍将受到严重限制.如果您的想法是在编译时,请不要阅读其余的答案
从用户级别(在运行时)?是的你可以.虽然,逻辑非常简单,但您只是想找到一种方法来实际创建和维护表达式树的不变量.要实现它需要一些工作.
所以,让我们来解决逻辑......为简单起见,让我们在这里定义一些基本术语以符合我们的意图
operator是一个最多需要2的函数,operands当被调用时,它产生的结果是另一个operandoperand在您的情况下,An 是一个感兴趣的对象,更确切地说是数字double.它可以由您或您生产expressionexpression是一个最多占用2 operands和operatora 的对象,并operand通过调用operator函数产生结果.我做了一些图纸来说明这个......
如您所见,箭头显示了知识的方向.
Operand知道所涉及的所有表达方式.Expression知道Operands生产出来.那么,让我们给他们一些身份......
让我们说,你创建的Operand 1,Operand 2,Operand 4.您开始按以下顺序构建此表达式树:
所创建的关系(一个Expression之间)Operand 1并且Operand 2其由下式表示Expression1.
Expression1用Operator它来构造它来产生它的结果,Operand 3
您将结果Operand 3与您创建的结果组合Operand 4成一个新表达式Expression2以产生另一个结果,Operand 5
现在,让我们看看当我们决定修改时会发生什么Operand 1.
如您所见,修改后的操作数将递归遍历并更新其结果取决于它的所有子表达式(无论是直接还是通过代理).
现在,我们已经有了这个非常简单的想法,我们该怎么做呢.有许多方法可以实现它,它越通用和灵活,性能就越差(在内存和速度方面)
我在下面做了一个简单的实现(显然,远非任何最佳).
template<typename T>
class Operand;
template<typename T>
class Expression {
std::shared_ptr<Operand<T>> m_operand1;
std::shared_ptr<Operand<T>> m_operand2;
std::shared_ptr<Operand<T>> m_result;
T (*m_operator)(const T&, const T&);
friend class Operand<T>;
public:
Expression(
T(*operator_func)(const T&, const T&),
std::shared_ptr<Operand<T>> operand_1,
std::shared_ptr<Operand<T>> operand_2) :
m_operand1(operand_1),
m_operand2(operand_2),
m_result(std::make_shared<Operand<T>>(T{})),
m_operator(operator_func)
{
}
void update(){
m_result->value() = m_operator(m_operand1->value(), m_operand2->value());
m_result->update();
}
std::shared_ptr<Operand<T>>& result() { return m_result; }
};
template<typename T>
class Operand {
T val;
std::vector<std::shared_ptr<Expression<T>>> expressions;
friend class Expression<T>;
public:
Operand(T value) : val(value) {}
T& value() { return val; }
void update(){
for(auto& x : expressions)
x->update();
}
static std::shared_ptr<Operand<T>> make(const T& t){
return std::make_shared<Operand<T>>(t);
}
static std::shared_ptr<Operand<T>>
relate(
T(*operator_func)(const T&, const T&),
std::shared_ptr<Operand<T>> operand_1,
std::shared_ptr<Operand<T>> operand_2
){
auto e = std::make_shared<Expression<T>>(operator_func, operand_1, operand_2);
operand_1->expressions.push_back( e );
operand_2->expressions.push_back( e );
e->update();
return e->result();
}
};
//template<typename T>
//double add(const double& lhs, const double& rhs){ return lhs + rhs; }
template<typename T>
T add(const T& lhs, const T& rhs){ return lhs + rhs; }
template<typename T>
T mul(const T& lhs, const T& rhs){ return lhs * rhs; }
int main()
{
using DOperand = Operand<double>;
auto d1 = DOperand::make(54.64);
auto d2 = DOperand::make(55.36);
auto d3 = DOperand::relate(add<double>, d1, d2);
auto d4 = DOperand::relate(mul<double>, d3, d2);
//---------------PRINT------------------------//
std::cout << "d1 = " << d1->value() << "\nd2 = " << d2->value()
<< "\nd3 = d1 + d2 = " << d3->value() << "\nd4 = d3 * d2 = "
<< d4->value() << std::endl;
//---------------UPDATE ONE VARIABLE------------------------//
std::cout << "\n\n====================\n" << std::endl;
std::cout << "changed d1 from " << d1->value() << " to ";
d1->value() = -863.2436356;
d1->update();
std::cout << d1->value() << "\n\n=======================\n\n";
//---------------PRINT------------------------//
std::cout << "d1 = " << d1->value() << "\nd2 = " << d2->value()
<< "\nd3 = d1 + d2 = " << d3->value() << "\nd4 = d3 * d2 = "
<< d4->value() << std::endl;
// *******************************************
std::cout << "\n\n\n\n\nSizeof(Operand<int>) = " << sizeof(Operand<int>)
<< "\nSizeof(Expression<int>) = " << sizeof(Expression<int>) << std::endl;
}
Run Code Online (Sandbox Code Playgroud)
输出是:
d1 = 54.64
d2 = 55.36
d3 = d1 + d2 = 110
d4 = d3 * d2 = 6089.6
====================
changed d1 from 54.64 to -863.244
=======================
d1 = -863.244
d2 = 55.36
d3 = d1 + d2 = -807.884
d4 = d3 * d2 = -44724.4
Run Code Online (Sandbox Code Playgroud)
在科利鲁看到它
对于简单integral类型,我的使用shared_ptr是一种矫枉过正,我实际上可以用普通指针做到这一点.但是这种实现倾向于概括于类型typename T.
其他要考虑的事情......
欢迎评论,批评和建议.:-)
| 归档时间: |
|
| 查看次数: |
642 次 |
| 最近记录: |