如何在"折叠表达式"中提供"短路评估"?

xml*_*lmx 6 c++ standards short-circuiting fold-expression c++17

#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!
}
Run Code Online (Sandbox Code Playgroud)

传统的&&运算符支持短路评估,因此false && (*pb = true)在运行时也可以,但以下两种情况都没有.

如何使短路评估也可在fold expressions

Bar*_*rry 15

这里的问题只是对实际发生的事情的误解.

如何在折叠表达式中进行短路评估

折叠表达式.(args && ... )遵循完全相同的规则(a && b && c && d).也就是说,d将仅评估a,b以及c所有评估为truthy.

这不是你的两个案件之间的实际差异.

false && (*pb = true);       // ok at runtime.
AndL(false, (*pb = true));   // error at runtime!
Run Code Online (Sandbox Code Playgroud)

虽然折叠表达式与非折叠表达式完全相同,但这两个语句之间存在一个重要区别.第一个是语句表达式,第二个是函数调用.并且必须在正文开始之前评估所有函数参数.

所以第二个相当于:

auto&& a = false;
auto&& b = (*pb = true);
(FORWARD(a) && FORWARD(b));
Run Code Online (Sandbox Code Playgroud)

这是导致问题的排序,而不是折叠表达式(注意:b之前可以评估a).

为了使这个透明,你真正需要的是懒惰的参数.这是几种语言(例如Scala)的功能,但不是C++中的功能.如果你需要懒惰,你可以做的最好的事情是将所有东西都包裹在lambda中:

template<typename... Args>
constexpr bool AndL(Args&&... args)
{
    return (... && FORWARD(args)());
}

AndL([]{ return false; }, [&]{ return *pb = true; });
Run Code Online (Sandbox Code Playgroud)

然后你可以使这个任意复杂 - 也许只是"解开"那些可调用的类型,否则假设它们是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; });
Run Code Online (Sandbox Code Playgroud)

但实际上,重点是函数参数评估在函数体之前,问题不在于折叠表达式本身.