In the following code sample, the if
statement depends on bool
template parameter, which is a compile-time constant. Compilers handle this code differently:
MSVC fails with link error (which is what I expected), because the template function in else
branch lacks specialisation for true
template parameter value (even though it is never called).
GCC and Clang both compile without issues and run-time behaviour is correct. This is obviously because they evaluate the if
statement at compile time and remove unused branches before linking.
The question is which behaviour is standard-compliant (or is it an undefined behaviour and both are correct in their own way)?
#include <iostream>
template<const bool condition>
struct Struct
{
void print()
{
if (condition)
{
std::cout << "True\n";
}
else
{
printIfFalse();
}
}
private:
void printIfFalse();
};
template <>
void Struct<false>::printIfFalse()
{
std::cout << "False\n";
}
int main()
{
Struct<true> withTrue{};
withTrue.print();
Struct<false> withFalse{};
withFalse.print();
return 0;
}
All compilers behave correctly.
Your program is ill-formed, no diagnostic required, because you are odr-using Struct<true>::printIfFalse
through the instantiation of Struct<true>::print()
required from the call in withTrue.print();
. A function that is odr-used outside of a discarded statement must have a definition in the program, see [basic.def.odr]/4, otherwise the program is ill-formed, no diagnostic required.
A discarded statement is what you get if you use if constexpr
in a template and the statement is not in the chosen branch. So, what you can do to make the program well-formed is to use if constexpr
instead of if
. This is a C++17 feature.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With