Weird evaluation order in chained functions

前端 未结 1 494
时光取名叫无心
时光取名叫无心 2021-01-20 19:47

I have this code, which is used to build a graphic interface on a LCD display on a controller; the code is compiled for 2 different architectures using both AVR and PIC32:

1条回答
  •  悲&欢浪女
    2021-01-20 20:07

    As mentioned in the comment, until C++17, the order of evaluation of unsequenced subexpressions in function arguments is unspecified, so it's not a compiler bug: both orderings are allowed (even resulting in undefined behaviour if those expressions result in more than one read/write to the same scalar variable, eg as in f(i++,i++)).

    Since C++17, postfix expressions (like function calls) evaluate left-to-right; the evaluation order of function arguments is still unspecified, but cannot interleave. So your code will always give the wanted result since C++17.

    As a workaround, you may let Label and friends also accept lambdas as parameters, to support lazy(and hence ordered) evaluation, something like:

    template
    auto constref_or_evaluate(T const& t) -> T const&
        { return t; }
    
    template
    auto constref_or_evaluate(T&& t) -> decltype(std::forward(t)())
        { return std::forward(t)(); }
    
    // the type of FishinoTftGui
    struct FishinoTftGuiType
    {
      // chainable members ...
    
      template
      auto Label(T&&... t) -> FishinoTftGuiType&
      {
        LabelImpl( constref_or_evaluate(std::forward(t))... );
    
        return *this;
      }
    
    private:
      // the original chainable member implementations ...
    
      void LabelImpl(int,int); //whatever
    };
    
    // to be used as
    
    FishinoTftGui
      .Label(1,2)
      .Label([]{return 3;},4);
    

    here the lambda in the second Label() will be always invoked after the first Label() had been fully evaluated.

    This has also the advantage of giving finer control on when a lazy expression is evaluated (say, the label could update the lazy parameter whenever the view is resized, etc...). So, it might be worth considering in >=C++17 code as well.

    As far as I can tell, this is just C++11; anyway, if you also want to pass l/rvalue reference parameters you'll need to write a forward_or_evaluate() function; this is perfectly doable in C++11, but it's a bit harder to implement.

    0 讨论(0)
提交回复
热议问题