折叠表达式短路

Short circuiting in fold expressions

这是一个基于我给出的自我回答的自我触发问题

seems a pretty convincing explanation of why short-circuiting of logical operators is available in fold expressions,以及用可变参数 包装折叠表达式似乎是非短路的事实(事实上,答案解释说,是函数调用触发了对所有参数的求值,在短路发生在函数体内之前。

但是,在我看来,以下代码证明(至少当折叠表达式中的参数为 2 时)不会发生短路:

#include <assert.h>
#include <optional>

constexpr auto all_r = [](auto const& ... ps){
    return [&ps...](auto const& x){
        return (ps(x) && ...);
    };
};

constexpr auto all_l = [](auto const& ... ps){
    return [&ps...](auto const& x){
        return (... && ps(x));
    };
};

constexpr auto has_value = [](std::optional<int> o){
    return o.has_value();
};
constexpr auto has_positive = [](std::optional<int> o){
    assert(o.has_value());
    return o.value() > 0;
};

int main() {
    assert(!(has_value(std::optional<int>{}) && has_positive(std::optional<int>{})));
    //assert(!(has_positive(std::optional<int>{}) && has_value(std::optional<int>{}))); // expectedly fails at run-time


    assert(!all_r(has_value, has_positive)(std::optional<int>{}));
    assert(!all_l(has_value, has_positive)(std::optional<int>{})); // I expected this to fail at run-time
    //assert(!all_r(has_positive, has_value)(std::optional<int>{}));
    //assert(!all_l(has_positive, has_value)(std::optional<int>{})); // I expected this to succeed at run-time
}

Pack && ... 的含义开始。

Cppreference 有一个非常易读的描述。

Pack && ... 变为 Pack1 && (Pack2 && (Pack3 && Pack4))) ... && Pack 变为 (((Pack1 && Pack2) && Pack3) && Pack4

在计算 && 时,我们在解析树的顶部从左到右计算。

对于 Pack&&... 的情况,这个顶级 &&Pack1 然后是 and 运算符,然后是 (Pack2 && (Pack3 && Pack4))&& 首先评估左侧,如果为假则停止。

对于 ...&&Pack 案例,顶层 && 在右边。它的左手是(((Pack1 && Pack2) && Pack3),右手是Pack4

但是为了确定左手是否正确,我们继续应用该规则。我们最终评估的第一个术语是... Pack1。如果它是假的,我们就不会费心评估其余部分。

虽然树的形状不同,但也没有想象中那么重要

  +
 / \
A   +
   / \
  B   C

      +
     / \
    +   C
   / \
  A   B

按顺序遍历时按相同顺序访问节点,左/右折叠只是切换生成这两个表达式树中的哪一个。

在某些情况下 left/right 折叠很重要,但 && 评估为 bool 的事情不是其中之一。

... && ps(x) 有四个谓词 a, b, c, d 扩展为

( ( a(x) && b(x) ) && c(x) ) && d(x)

这导致了这个评估顺序:a b c d

ps(x) && ... 扩展为

a(x) && ( b(x) && ( c(x) && d(x) ) )

这导致相同的评估顺序:a b c d

这不会改变短路的任何内容;一旦一个错误,评估就会停止。