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<typename T>
auto constref_or_evaluate(T const& t) -> T const&
{ return t; }
template<typename T>
auto constref_or_evaluate(T&& t) -> decltype(std::forward<T>(t)())
{ return std::forward<T>(t)(); }
// the type of FishinoTftGui
struct FishinoTftGuiType
{
// chainable members ...
template<typename... T>
auto Label(T&&... t) -> FishinoTftGuiType&
{
LabelImpl( constref_or_evaluate(std::forward<T>(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.
与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…