So now I'm wondering whether this code should compile:
Are you "wondering" whether this complete program "should" compile?
void f();
void g() { auto ff = &f; }
int main() {}
If you assume it will compile, "how many times" do you conclude the code will compile? (Is it a single truth, or a double independent truth, that it should compile?)
f()
is "used" in a non-use way (the address is assigned to a local variable that is optimized away).
g():
ret
g()
itself isn't even used
Any "dumb" linker could see that because g()
isn't needed, f()
isn't needed; that is assuming a "dumb" compiler would assume that f()
is even needed in g()
!
Yet an undeclared function (f()
) was clearly ODR-used. Did you expect that implementation behavior, or do you "wonder" and ask questions about it?
If you expected no error, you probably should rethink that whole "ODR-use violation gets a pass by the compiler makes me wondering" thing:
Implementation don't diagnose the lack of definition they don't need to make a working program.
What they need depends on the cleverness of the implementation, as obviously you can make the argument 1. above way more complicated if the non-need of f()
in g()
is buried in complex code. I intentionally gave a trivial example, one valid at -O0
.
[Note: this is assembly for g()
at level -O0
:
g():
push rbp
mov rbp, rsp
mov QWORD PTR [rbp-8], OFFSET FLAT:_Z1fv
nop
pop rbp
ret
No dependency on symbol f()
as you can see.]
Knowledge depends on optimization level and on the amount of data provided at code generation time (compiling many translation units at the same time might help).
For non virtual functions, the function is either needed (that is named) by a needed thing (another function or a global object initialized with that function address).
For virtual functions, getting knowledge is much more difficult: knowing that an overrider is not going to be called isn't as trivial as with a function that is not an overrider (including a non virtual function), as an overrider can be called via "late binding", when a virtual call is made on a (g)lvalue.
The compiler may or may not be able to determine precisely which virtual functions are never needed, depending on the complexity of the program (the halting program issue shows that they will never to know exactly for all programs), but non static member functions of objects that are never instantiated obviously are never needed.
Foo
's destructor? Fails to compile if you actually make aFoo
: godbolt.org/z/ydHIOl – Godparent~Foo
is odr-used simply by being declared. I could very well be wrong, but that's what I make of it. – Tetramethyldiarsine