Consider the following program (and its alternative in the comment) in C++17:
#include<iostream>
void a(int) {
std::cout << "a\n";
}
void b(int) {
std::cout << "b\n";
}
int main() {
using T = void(*)(int);
T f = a;
(T(f))((f=b,0)); // alternatively: f((f=b,0))
}
With -O2
option, Clang 9.0.0 prints a
and GCC 9.2 prints b
. Both warn me about unsequenced modification and access to f
. See godbolt.org.
My expectation was that this is program has well-defined behavior and will print a
, because C++17 guarantees that the left-hand expression of the call (T(f))
is sequenced before any evaluation of the arguments. Because the result of the expression (T(f))
is a new pointer to a
, the later modification of f
should have no impact on the call at all. Am I wrong?
Both compilers give the same output if I use f((f=b,0));
instead of (T(f))((f=b,0));
. Here I am slightly unsure about the undefined behavior aspect. Would this be undefined behavior because f
still refers to the declared function pointer after evaluation, which will have been modified by the evaluation of the arguments and if so, why exactly would that cause undefined behavior rather than calling b
?
I have asked a related question with regards to order of evaluation of non-static member function calls in C++17 here. I am aware that writing code like this is dangerous and unnecessary, but I want to understand the details of the C++ standard better.
Edit: GCC trunk now also prints a
after the bug filed by Barry (see his answer below) has been fixed. Both Clang and GCC trunk do still show false-positive warnings with -Wall
, though.