Consider a function template func
that is very performance critical. It can be instantiated with T=Type1
or some other type. Part of the function logic depends on T
it is instantiated with.
One can either explicitly use a if constexpr
(Code B) or use a vanilla if
instead (Code A), while compiler probably optimizes the code.
However, I wonder, how the implementation without constexpr
(Code A) is any different? Isn't the compiler capable of detecting which branch of if
(in Code A) to use at compile time while instantiating? Can it still (for Code A) generate a less efficient code?
Code A. Without if constexpr
:
template<class T>
void func(T argument)
{
// some general type-independent logic
if (std::is_same<Type1,T>::value)
{
// do something
}
else
{
// do something else
}
// some general type-independent logic
}
Code B. With if constexpr
:
template<class T>
void func(T argument)
{
// some general type-independent logic
if constexpr (std::is_same<Type1,T>::value)
{
// do something
}
else
{
// do something else
}
// some general type-independent logic
}
Both codes A & B compile, as do something
and do something else
are well-formed for any T
.
There are some similar-sounding questions:
constexpr if
needed? – this one answers when constexpr
is required.if
and constexpr if
– just lists the differencesThe aforementioned questions do not answer if Code B is preferable to Code A for some reason (when both branches are well-formed anyway).
The only advantage I see would be to tell the programmer explicitly that this if
is compile-time; however, I would say the conditional expression is self-explanatory.
if constexpr
is not intended about optimization. Compilers are very good at optimizing away a branch that is if (true)
or if (false)
(since we're talking about constant expressions, that is what it boils down to). Here is a godbolt demo of the example in OP - you'll note that both gcc and clang, even on -O0
, do not emit a branch for a simple if
.
if constexpr
is all about ensuring that only one branch of the if
is instantiated. This is hugely important and valuable for writing templates - because now we can actually write conditionally compiling code within the body of the same function instead of writing multiple artificial functions just to avoid instantiation.
That said, if you have a condition that is a known constant expression - just always use if constexpr
, whether or not you need the instantiation benefit. There is no downside to such a decision. It makes it clearer to readers that indeed this condition is constant (since otherwise it wouldn't even compile). It will also force the evaluation of the expression as a constant (a slight variant leads gcc to emit a branch at -O0
, thought not at -O1
), which with the coming addition of is_constant_evaluated()
may become more important in the long run (possibly even negating my opening paragraph).
The only advantage I see would be to tell the programmer explicitly that this if is compile-time; however, I would say the conditional expression is self-explanatory.
To address this specifically, yes, std::is_same<X, Y>::value
is "self-explanatory" that it is a constant expression... because we happen to be familiar with std::is_same
. But it's less obvious whether foo<X>::value
is a constant expression or whether foo<X>() + bar<Y>()
is a constant expression or anything more arbitrarily complicated than that.
It's seeing if constexpr
that makes the fact that it's compile-time self-explanatory, not the content of the condition itself.
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