为什么在这些折叠表达式中使用 std::min 是未定义的行为?

问题描述

感谢 Can I implement max(A,max(B,max(C,D))) using fold expressions?,我知道一种在折叠表达式中使用 std::min 的工作方法(下面的min2)。但是,我很好奇为什么下面的方法 min1min3 被认为是未定义的行为(似乎给出了警告)?

根据我的理解,表达式应该在两种情况下从左到右求值,不断更新 myMin 并将最后一个值分配回 myMin。此外,最终答案在 gcc 和 clang 上也总是正确的。

template <typename... Args>
auto min1(const Args&... anArgs) {
    constexpr size_t N = sizeof...(anArgs);
    auto myMin = std::get<0>(std::tuple(anArgs...));
    myMin = std::get<N-1>(std::tuple((myMin = std::min(myMin,anArgs))...));
    return myMin;
}

template <typename... Args>
auto min2(const Args&... anArgs) {
    return std::min({anArgs...});
}

template <typename... Args>
auto min3(const Args&... anArgs) {
    auto myMin = (anArgs,...);
    myMin = ((myMin = std::min(myMin,anArgs)),...);
    return myMin;
}

警告是:

main.cpp: In instantiation of 'auto min1(const Args& ...) [with Args = {int,int,int}]':
main.cpp:26:30:   required from here
main.cpp:8:45: warning: operation on 'myMin' may be undefined [-Wsequence-point]
    8 |     myMin = std::get<N-1>(std::tuple((myMin = std::min(myMin,anArgs))...));
      |                                      ~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~
main.cpp:8:45: warning: operation on 'myMin' may be undefined [-Wsequence-point]
main.cpp: In instantiation of 'auto min3(const Args& ...) [with Args = {int,int}]':
main.cpp:29:30:   required from here
main.cpp:20:10: warning: left operand of comma operator has no effect [-Wunused-value]
   20 |     auto myMin = (anArgs,...);
      |          ^~~~~
main.cpp:20:10: warning: left operand of comma operator has no effect [-Wunused-value]
main.cpp:21:11: warning: operation on 'myMin' may be undefined [-Wsequence-point]
   21 |     myMin = ((myMin = std::min(myMin,...);

Coliru Link

最后,我正在研究替代方法(特别是 min1)的原因是因为我正在尝试使用不推荐使用逗号运算符的 3rd 方库,我想知道这是否仍然可以使用折叠表达式来解决.

解决方法

您实际上没有任何未定义的行为。请注意,警告说:

警告:对'myMin'的操作可能未定义[-Wsequence-point]

(我的重点)

min1min3 中的此警告来自表达式

((myMin = std::min(myMin,anArgs))...)

如果参数包有 3 个元素,就像你的情况一样,这个表达式将被实例化为:

((myMin = std::min(myMin,__anArgs0)),((myMin = std::min(myMin,__anArgs1)),(myMin = std::min(myMin,__anArgs2))))

其中 __anArgs0 等只是由实现生成的标识符。您可以在 cppinsights 上看到。

我可以看到这个表达式中没有未排序的操作。我不太确定为什么会生成这些警告,但我很确定它们是误报。

, 之间的表达式是未排序的,这是真的,因此您会收到整个表达式可能未定义的警告。但是,由于您计算 3 个不同的 std::min 调用的顺序无关紧要,因此您没有未定义的行为,并且您总是会得到相同的结果。

虽然编译器不知道这些信息,所以它会给你警告。

您不会收到带有 min2 的警告,因为参数位于初始化器列表中,其中参数的计算顺序已明确定义。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...