Thanks to Can I implement max(A, max(B, max(C, D))) using fold expressions? I'm aware of one working approach to use std::min in a fold expression(min2
below). However, I'm curious why the below approaches min1
and min3
are considered undefined behavior(seemingly given the warning)?
Per my understanding the expression should evaluate in both cases from left to right, constantly updating myMin
and assign the last value back to myMin
. Additionally, the final answer is also always correct on both gcc and 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;
}
The warnings are:
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)), ...);
Coliru Link
Finally, the reason I'm looking into alternate approaches(specifically min1) is because I'm trying to use a 3rd party library for which the comma operator is deprecated and I was wondering if this can still be solved using fold expressions.
You don't actually have any undefined behavior. Note that the warnings say:
warning: operation on 'myMin' may be undefined [-Wsequence-point]
(my emphasis)
This warning, in both min1
and min3
, comes from the expression
((myMin = std::min(myMin, anArgs))...)
If the parameter pack has 3 elements as it does in your case, this expression will get instantiated as:
((myMin = std::min(myMin, __anArgs0)) ,
((myMin = std::min(myMin, __anArgs1)) ,
(myMin = std::min(myMin, __anArgs2))))
where __anArgs0
etc are just identifiers generated by the implementation. You can see this on cppinsights.
There's no unsequenced operations in this expression that I can see. I'm not really sure why these warnings are being generated, though I'm fairly sure they're false positives.
The expressions between the ,
are unsequenced, that is true, and hence you get the warning that the entire expression may be undefined. However, since it doesn't matter in which order you compute the 3 different std::min
calls, you don't have undefined behavior, and you are always going to get the same result.
The compiler doesn't know this information though, and so it gives you the warning.
You don't get a warning with min2
because the arguments are within a initalizer list, where the order of evaluation of the arguments is well defined.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With