为什么在这些折叠表达式中使用 std::min 是未定义的行为?
Why is the use of std::min in these fold expressions undefined behavior?
感谢 我知道了一种在折叠表达式中使用 std::min 的工作方法(下面的 min2
)。但是,我很好奇为什么下面的方法 min1
和 min3
被认为是未定义的行为(似乎给出了警告)?
根据我的理解,表达式应该在两种情况下从左到右求值,不断更新 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, 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, anArgs)), ...);
最后,我正在研究替代方法(特别是 min1)的原因是因为我正在尝试使用不推荐使用逗号运算符的第 3 方库,我想知道是否仍然可以使用以下方法解决这个问题折叠表达式。
您实际上没有任何未定义的行为。请注意,警告说:
warning: operation on 'myMin' may be undefined [-Wsequence-point]
(我的重点)
此警告在 min1
和 min3
中均来自表达式
((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
的警告,因为参数在初始化器列表中,其中参数的评估顺序已明确定义。
感谢 min2
)。但是,我很好奇为什么下面的方法 min1
和 min3
被认为是未定义的行为(似乎给出了警告)?
根据我的理解,表达式应该在两种情况下从左到右求值,不断更新 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, 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, anArgs)), ...);
最后,我正在研究替代方法(特别是 min1)的原因是因为我正在尝试使用不推荐使用逗号运算符的第 3 方库,我想知道是否仍然可以使用以下方法解决这个问题折叠表达式。
您实际上没有任何未定义的行为。请注意,警告说:
warning: operation on 'myMin' may be undefined [-Wsequence-point]
(我的重点)
此警告在 min1
和 min3
中均来自表达式
((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
的警告,因为参数在初始化器列表中,其中参数的评估顺序已明确定义。