Why do compiler options impact selection of template implementation?
Your code is ill-formed, no diagnostic required. So different behaviors at different optimization levels are possible.
[temp.expl.spec]
6 If a template, a member template or a member of a class template is explicitly specialized then that specialization shall be declared before the first use of that specialization that would cause an implicit instantiation to take place, in every translation unit in which such a use occurs; no diagnostic is required. If the program does not provide a definition for an explicit specialization and either the specialization is used in a way that would cause an implicit instantiation to take place or the member is a virtual member function, the program is ill-formed, no diagnostic required. An implicit instantiation is never generated for an explicit specialization that is declared but not defined.
The function template is specialized in one TU, but the other doesn't have a specialization declaration available. It's quite likely an aggressive optimizer chooses the implicit instantiation (that is available inline) instead of finding the one you created elsewhere. The solution is to declare that your specialization exists in the header.
You have undefined behaviour because of ODR issues.
ODR says that there should be only one definition for each symbol. Inline and template functions can multiple definitions but must have the same implementation, token by token. No diagnostic required if this rule is broken.
When compiling your example, the compiler will instantiate your function. Look at this:
template <int>
void func() { std::cerr << "default impl\n"; } // normally no impl here
int main(void)
{
func<1>();
func<2>();
return 0;
}
This is what the compiler sees. It cannot see other cpp files. The compiler will instantiate the templates and create additional definition for your functions.
Then your other cpp file will provide another definition that is different.
The solution to this is to forward declare the specializations in your header:
template<> void func<1>();
template<> void func<2>();
This will tell the compiler that the specializations are declared elsewhere, and not to instantiate the default one.