In my code I have a function template which tests if certain pointers are convertible, somewhat like this:
template <typename T, typename U> bool test() {
return requires(T* p) { static_cast<U*>(p); };
}
Then in X.cpp I have definitions for my classes (included their headers) and call test<A, B>()
:
struct A {};
struct B : A {};
bool foo() {
return test<A, B>(); // Should return true because static_cast<B*>(<ptr-to-A>) is valid
}
and in Y.cpp I only have forward declarations but also call test<A, B>()
:
struct A;
struct B;
bool bar() {
return test<A, B>(); // Will return false because A and B are unrelated at this point
}
The call to test<A, B>()
in Y.cpp is invalid and breaks my preconditions for test
, but is legal C++ AFAIK.
If I comile this program (using Clang) with optimizations, foo()
returns true as expected, but if I compile without optimizations, foo()
returns false, probably because test
is not inlined and linked against the version compiled in Y.cpp.
I am aware this is not great design, but unfortunately my entire codebase depends on this.
I would like to know if this is even well-formed C++ or if I perhaps violate ODR here and how I can practically find out which translation unit's version of test<A, B>
is selected by the linker in foo
to debug the program. In the real code the calls to test
are very convoluted and happen deep inside of template instantiations.
I did read this question but the answers only focus on that the standard does not specify it.
I admit I don't completely understand your issue, and not sure if the following helps. Though, to find out where the template is instantiated you can make instantiation fail (but make sure it fails only on instantiation not earlier already).
Resulting error message reports the point of instantiation via
required from here
: