How to make `short-circuit evaluation` also available in `fold expressions`?

前端 未结 1 367
我在风中等你
我在风中等你 2021-02-04 19:30
#include 

#define FORWARD(arg)\\
std::forward(arg)

template
constexpr bool AndL(Args&&... args)         


        
1条回答
  •  北荒
    北荒 (楼主)
    2021-02-04 20:02

    The problem here is just a misconception of what's actually happening.

    How to make short-circuit evaluation also available in fold expressions?

    It is available in fold expressions. (args && ... ) follows the exactly the same rules as (a && b && c && d). That is, d will only be evaluated if a, b, and c all evaluate to truthy.

    That's not the actual difference between your two cases.

    false && (*pb = true);       // ok at runtime.
    AndL(false, (*pb = true));   // error at runtime!
    

    While fold expressions do exactly the same thing as their non-fold counterparts, there's one important difference between these two statements. The first is just a statement-expression, the second is a function call. And all function arguments must be evaluated before the start of the body begins.

    So the second is equivalent to:

    auto&& a = false;
    auto&& b = (*pb = true);
    (FORWARD(a) && FORWARD(b));
    

    It's that ordering that is causing the problem, not the fold expression (note: b could be evaluated before a).

    In order to make this transparent, what you really need are lazy arguments. This is a feature in several languages (e.g. Scala), but not in C++. If you need laziness, the best you could do is wrap everything in a lambda:

    template
    constexpr bool AndL(Args&&... args)
    {
        return (... && FORWARD(args)());
    }
    
    AndL([]{ return false; }, [&]{ return *pb = true; });
    

    You could then make this arbitrarily complex - maybe only "unwrap" those types that are callable, otherwise assume that they're bool:

    template ::value, int> = 0>
    bool unwrap(T&& val) { return std::forward(val)(); }
    
    template ::value, int> = 0>
    bool unwrap(T&& val) { return std::forward(val); }
    
    template
    constexpr bool AndL(Args&&... args)
    {
        return (... && unwrap(FORWARD(args)));
    }
    
    AndL(false, [&]{ return *pb = true; });
    

    But really, the main point is that function argument evaluation precedes the function body, and the issue is not the fold expression itself.

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