Reputation: 9196
I wrote the following C++17 code:
constexpr bool gDebug = true;
template <typename T> constexpr const T& Select(const bool pCondition, const T& a, const T& b)
{
if constexpr (pCondition)
{
return a;
}
else
{
return b;
}
}
Then I called it like this:
int c = Select<QString>(gDebug, a, b); // In .cpp
I get error: ‘pCondition’ is not a constant expression
for the if constexpr
line.
Why? Shouldn't this work?
Upvotes: 1
Views: 3634
Reputation: 61
All other answers are correct, and not quite saying what I've wanted to know when tripping on this. For a constexpr function to work in both constexpr (compile-time) and variable (runtime) contexts, you cannot have anything in the function that restricts it to constexpr context as Patrick points out. More to the point, you don't need any such restriction for the function to work in constexpr context. The compiler figures it out - it now has freedom (as required) to evaluate the function in either constexpr or runtime context. So the constexpr
in if constexpr (pCondition)
can simply be taken out.
Upvotes: 0
Reputation: 1417
Barry is correct but I think it is not really where you are going wrong in your understanding.
template <typename T> constexpr const T& Select(const bool pCondition, const T& a, const T& b)
means that the function call is evaluable at compile time if the parameters are. But the function can be called with parameters not known at compile time. In this case the function call itself is not a constant expression and will be evaluated at run-time. It is still pure though in the sense of functional programming and it is noexcept
by the way.
but if constexpr (pCondition) {...
really means the expression must be evaluable at compile time.
Hence, because a constexpr function can be called with parameters unknown at compile time, it cannot contain expressions declared constexpr
if they depend on those parameters.
Upvotes: 1
Reputation: 303636
Why? Shouldn't this work?
No, it shouldn't. pCondition
is not a constant expression. I can see why this might be confusing, since pCondition
is const
- but the term constant expression refers to it being able to be evaluated at compile time. That is, not const
but really constexpr
.
Function parameters are not constant expressions. The fact that you happen to pass a compile-time constant is immaterial, since you could just as easily pass a runtime variable that you read from stdin or something.
if constexpr
requires a constant expression, so you really just want if
there. Or, you need to lift the condition to be a constant expression - such as by making it a template parameter:
template <bool pCondition, typename T>
constexpr const T& Select(const T& a, const T& b)
{
if constexpr (pCondition) // now okay
{
return a;
}
else
{
return b;
}
}
int c = Select<qDebug>(a, b);
or you could require that the parameter be a value encoded into a type:
template <typename Boolean, typename T>
constexpr const T& Select(Boolean pCondition, const T&, const T&);
constexpr std::true_type qDebug{}; // true_type, not bool = true
int c = Select(qDebug, a, b); // okay
Upvotes: 7