这里的问题只是对实际发生的事情的误解。
如何在折叠表达式中也可以使用短路评估?
它在折叠表达式中可用。(args && ... )
遵循与 完全相同的规则(a && b && c && d)
。也就是说,d
仅当a
、b
和c
全部评估为真时才会评估。
这不是你的两个案例之间的实际区别。
false && (*pb = true); // ok at runtime.
AndL(false, (*pb = true)); // error at runtime!
虽然折叠表达式与非折叠表达式的作用完全相同,但这两个语句之间存在一个重要区别。第一个只是一个语句表达式,第二个是函数调用。并且必须在主体开始之前评估所有函数参数。
所以第二个相当于:
auto&& a = false;
auto&& b = (*pb = true);
(FORWARD(a) && FORWARD(b));
这是导致问题的排序,而不是折叠表达式(注意:b
可以在之前评估a
)。
为了使这一点透明,您真正需要的是惰性参数。这是几种语言(例如Scala)的一个特性,但在 C++ 中没有。如果你需要懒惰,你能做的最好的就是把所有东西都包装在一个 lambda 中:
template<typename... Args>
constexpr bool AndL(Args&&... args)
{
return (... && FORWARD(args)());
}
AndL([]{ return false; }, [&]{ return *pb = true; });
然后你可以使这个任意复杂 - 可能只“解包”那些可调用的类型,否则假设它们是 bool:
template <class T, std::enable_if_t<std::is_invocable<T>::value, int> = 0>
bool unwrap(T&& val) { return std::forward<T>(val)(); }
template <class T, std::enable_if_t<std::is_convertible<T, bool>::value, int> = 0>
bool unwrap(T&& val) { return std::forward<T>(val); }
template<typename... Args>
constexpr bool AndL(Args&&... args)
{
return (... && unwrap(FORWARD(args)));
}
AndL(false, [&]{ return *pb = true; });
但实际上,重点是函数参数评估在函数体之前,问题不在于折叠表达式本身。