double d1, d2
。我们希望第三个对象(double d3
)获取d1+d2
的值,这样如果我们更改d1
或d2
,那么d3
会自动获得新的d1+d2
值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;
}
。我们怎样才能用C ++做到这一点?
这就是我的意思:
$parts = parse_url('http://example.com/sfm?dir=uploads/sfm/c4ca4238a0b923820dcc509a6f75849b/dir');
parse_str($parts['query'], $query);
echo $query['dir'];
感谢。
答案 0 :(得分:11)
您可以创建一个包装器,作为lambda:
double d1 = 0, d2 = 0;
auto l = [&](){ return d1 + d2; };
l(); // 0
d1 = 40;
d2 = 2;
l(); // 42
如果您希望所有变量具有相同的类型,您可以添加类型 - 擦除包装器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
答案 1 :(得分:9)
您的问题是参数绑定的经典动机。
#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';
}
bind和reference wrappers有助于实现您的功能。
答案 2 :(得分:7)
编写一个包装器,它将存储指向double
的指针(如原始问题中所推荐的那样)。请注意,如果double
s超出范围但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
}
答案 3 :(得分:5)
在编译时,不,充其量,你最终会得到一些令人讨厌的模板和宏黑客仍将受到严重限制。如果您的想法是在编译时,请不要阅读其余的答案
从用户级别(在运行时)?是的你可以。虽然,逻辑非常简单,但您只是想找到一种方法来实际创建和维护表达式树的不变量。它需要一些工作才能实现它。
所以,让我们来解决逻辑...... 为简单起见,让我们在这里定义一些基本术语以适应我们的意图
operator
是一个最多需要2 operands
的函数,以便在调用时生成另一个operand
operand
是您感兴趣的对象,在您的情况下,是数字,更准确地说是double
。它可以由您或expression
expression
是一个最多需要2 operands
和operator
的对象,并通过调用operand
函数生成operator
。< / LI>
我做了一些图纸来说明这个......
如您所见,箭头显示了知识的方向。
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;
}
输出是:
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
对于简单的integral
类型,我对shared_ptr
的使用是一种矫枉过正,我实际上可以用普通指针来做这件事。但是这种实现倾向于概括typename T
的类型。
要考虑的其他事情......
欢迎评论,批评和建议。 : - )
答案 4 :(得分:4)
如果不将d1
和d2
的类型更改为可观察的内容,则无法执行此操作。你可以这样做。
#include <iostream>
#include <boost/signals2.hpp>
class ObservableDouble
{
public:
boost::signals2::signal<void()> sigChanged;
void operator=( double val )
{
d = val;
sigChanged();
}
operator double() const { return d; }
private:
double d;
};
int main()
{
ObservableDouble d1;
ObservableDouble d2;
double d3;
auto add = [&](){ d3 = d1 + d2; };
d1.sigChanged.connect( add );
d2.sigChanged.connect( add );
d1 = 4.0;
d2 = 7.0;
std::cout << d3 << std::endl; // prints 11
d2 = -4.0;
std::cout << d3 << std::endl; // prints 0
}
<强> Live on Coliru 强>