#include <type_traits>
#define FORWARD(arg)\
std::forward<decltype(arg)>(arg)
template<typename... Args>
constexpr bool AndL(Args&&... args)
{
return (... && FORWARD(args));
}
template<typename... Args>
constexpr bool AndR(Args&&... args)
{
return (FORWARD(args) && ...);
}
int main()
{
bool* pb = nullptr;
false && (*pb = true); // ok at runtime.
AndL(false, (*pb = true)); // error at runtime!
AndR(false, (*pb = true)); // error at runtime!
}
The traditional &&
operator supports short-circuit evaluation, so false && (*pb = true)
will be ok at runtime, but the following two cases are not.
How to make short-circuit evaluation also available in fold expressions
?
The problem here is just a misconception of what's actually happening.
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 ifa
,b
, andc
all evaluate to truthy.That's not the actual difference between your two cases.
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:
It's that ordering that is causing the problem, not the fold expression (note:
b
could be evaluated beforea
).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:
You could then make this arbitrarily complex - maybe only "unwrap" those types that are callable, otherwise assume that they're bool:
But really, the main point is that function argument evaluation precedes the function body, and the issue is not the fold expression itself.