Why does GCC and Clang produce completely different outputs for this variadic template expansion? #203833
🏷️ Discussion TypeQuestion 💬 Feature/Topic AreaC++ / Templates & Undefined Behavior BodyHey C++ folks, I was playing around with variadic template pack expansion and side-effects in fold expressions, and I hit a case where GCC and Clang disagree completely on output. Consider this snippet (compiled with #include <iostream>
int counter = 0;
int step() {
return ++counter;
}
template<typename... Args>
void print_order(Args... args) {
int arr[] = { (args + step())... };
for (int x : arr) {
std::cout << x << " ";
}
std::cout << "\n";
}
int main() {
// Expected output?
print_order(10, 20, 30);
return 0;
}
What I expect:Since array initialization lists in C++ ( What actually happens:
Is Guidelines
|
Replies: 1 comment 1 reply
|
This is expected behavior, not a compiler bug — the confusion comes from mixing up two different guarantees. The left-to-right evaluation guarantee you're thinking of applies to braced-init-list element evaluation order (each element of But that guarantee only sequences the elements relative to each other — it says nothing about the order of evaluation of the two operands within a single element, i.e. So for each array element
Actually to be precise about your specific numbers (13 22 31): that pattern is consistent with This is not related to parameter pack expansion order at all — To get guaranteed, portable left-to-right pairing, force sequencing explicitly, e.g.: or cleaner, decouple the side effect from the expression entirely: or use an immediately-invoked lambda per element to force order: Any of these removes the unspecified-order operand pairing and will give you consistent |
This is expected behavior, not a compiler bug — the confusion comes from mixing up two different guarantees.
The left-to-right evaluation guarantee you're thinking of applies to braced-init-list element evaluation order (each element of
{ ... }is sequenced before the next). That part is correctly honored by all three compilers: element 0 (args[0] + step()) is fully evaluated before element 1, which is fully evaluated before element 2.But that guarantee only sequences the elements relative to each other — it says nothing about the order of evaluation of the two operands within a single element, i.e.
args + step(). The order of evaluating the left operand (args) vs. the right operand (step()