Consider the following definition:
template<int n> struct Foo;
template<> struct Foo<1> { void fn1(); };
template<> struct Foo<2> { void fn2(); };
Foo<1>
has a member function fn1
and similarly Foo<2>
has fn2
. We can use these functions, selecting the appropriate function to call using an if constexpr
expression:
template<int n>
void test_template() {
Foo<n> foo;
if constexpr (n==1) {
foo.fn1();
} else {
foo.fn2();
}
}
template void test_template<1>(); //Interested in `Foo<1>`
This works perfectly on latest Clang, GCC, and MSVC.
We can think about doing functionally the same thing, but with a constexpr
expression:
constexpr int n = 1; //Interested in `Foo<1>`
void test_constexpr() {
Foo<n> foo;
if constexpr (n==1) {
foo.fn1();
} else {
foo.fn2();
}
}
This fails (on all three compilers)! Specifically, all compilers' errors show that they are attempting to compile foo.fn2()
.
Is this behavior correct? If so, why? It seems to me like the compilation should be similar, and that the second case should work too!
EDIT: several other questions have been asked about if constexpr
in the context of template
instantiations (see comments). However, this is specifically about if constexpr
without template
s. Indeed, the template
version above works as I'd expect.
According to the C++ documentation:
If the value is
true
, then statement-false is discarded (if present), otherwise, statement-true is discarded.
However, it also says:
Outside a template, a discarded statement is fully checked.
if constexpr
is not a substitute for the#if
preprocessing directive
The question is why this should be the case?
(Tagging this C++17, since if constexpr
is a C++17 feature, though of course I am actually using C++20, and soon C++23 . . .)