连接三个不同的对象

时间:2016-04-30 14:02:22

标签: c++ c++11 c++14 shared-ptr

几个小时前我问了一个关于连接矢量的两个元素的类似问题。现在,我想让我的问题更加笼统。假设我们有两个类型为double的对象,即double d1, d2。我们希望第三个对象(double d3)获取d1+d2的值,这样如果我们更改d1d2,那么d3会自动获得新的d1+d2int 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'];

感谢。

5 个答案:

答案 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';
}

bindreference 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 operandsoperator的对象,并通过调用operand函数生成operator。< / LI>

我做了一些图纸来说明这个......

Expression Tree

如您所见,箭头显示了知识的方向。

  • Operand知道其所涉及的所有表达。
  • Expression知道它产生的Operands

所以,让我们给他们一些身份......

Expression Tree

我们说,您创建了Operand 1Operand 2Operand 4。您开始按以下顺序构建此表达式树:

  1. 您在ExpressionOperand 1之间创建了一个由Operand 2表示的关系(Expression1)。

  2. Expression1使用其构建的Operator来生成结果Operand 3

  3. 您将生成的Operand 3与创建的Operand 4合并到一个新的表达式Expression2中,以生成另一个结果Operand 5

  4. 现在,让我们看看当我们决定修改Operand 1时会发生什么。

    Effect of modifying one Operand

    正如您所看到的,修改后的操作数将以递归方式通过并更新其结果取决于它的所有子表达式(无论是直接还是通过代理)。

    现在,我们已经有了这个非常简单的想法,我们该怎么做呢。 有许多方法可以实现它,它越通用和灵活,它的性能就越低(在内存和速度方面)

    我在下面做了一个简单的实现(显然,远非任何最佳)。

    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
    

    Live on Coliru

    对于简单的integral类型,我对shared_ptr的使用是一种矫枉过正,我实际上可以用普通指针来做这件事。但是这种实现倾向于概括typename T的类型。

    要考虑的其他事情......

    • API选择
    • 内存使用
    • 避免周期检测(无限递归更新)
    • ...等

    欢迎评论,批评和建议。 : - )

答案 4 :(得分:4)

如果不将d1d2的类型更改为可观察的内容,则无法执行此操作。你可以这样做。

#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