Why Is Comparing Two Parameters of a Constexpr Function Not a Constant Condition for Static Assertion

Why is comparing two parameters of a constexpr function not a constant condition for static assertion?

A constexpr function can also be invoked with arguments evaluated at run-time (in that case, it just gets executed just like any regular function). See, for instance, this live example.

A static_assert(), on the other hand, strictly requires its condition to be a constant expression that can be evaluated at compile time.

Comparing constexpr function parameter in constexpr-if condition causes error

From constexpr if:

In a constexpr if statement, the value of condition must be a
contextually converted constant expression of type bool.

Then, from constant expression:

Defines an expression that can be evaluated at compile time.

Obviously, i == 5 is not a constant expression, because i is a function parameter which is evaluated at run time. That is why the compiler complains.

When you use a function:

constexpr bool test_int_no_if(const int i) { return (i == 5); }

then it might be evaluated during the compile time depending on whether it's parameter is known at compile time or not.

If i is defined like:

constexpr int i = 5;

then the value of i is known during the compile time and test_int_no_if might be evaluated during the compile too making it possible to call it inside static_assert.

Also note, that marking function parameter as const does not make it a compile time constant. It just means that you cannot change the parameter inside the function.

Integral template parameter. error: use of 'this' in a constant expression

std::array::size, even though a constexpr function, isn't a static function. This means it requires an object instance to be invoked.

In this particular case, at isn't constexpr, therefore this isn't either and in turn _a. Since _a isn't constexpr, the result of _a.size() isn't constexpr too.

† Even if it is, the body still isn't considered constexpr.

C++11 constexpr function pass parameter

A constexpr function and a constexpr variable are related, but different things.

A constexpr variable is a variable whose value is guaranteed to be available at compile time.

A constexpr function is a function that, if evaluated with constexpr arguments, and behaves "properly" during its execution, will be evaluated at compile time.

If you pass a non-constexpr int to a constexpr function, it will not magically make it evaluated at compile time. It will, however, be allowed to pass the constexprness of its input parameters through itself (normal functions cannot do this).

constexpr on functions is a mixture of documentation and restriction on how they are written and instructions to the compiler.

The reason behind this is to allow the same function to be evaluated both at compile time, and at run time. If passed runtime arguments, it is a runtime function. If passed constexpr arguments, it may be evaluated at compile time (and will be if used in certain contexts).

Note that consteval may be what you are looking for for a function. But maybe not.

You are getting errors because by passing in runtime values, you cannot get a compile time value out.

There are ways around this. My favorite is a std::variant of std::integer_constant; you can pick which is active at runtime, then std::visit to get the compile time constant. The downside is that this can generate a lot of code really easily.

template<auto I>
using constant_t=std::integral_constant<decltype(I),I>;
template<auto I>
constexpr constant_t<I> constant_v={};
template<auto...Is>
using var_enum_t=std::variant<constant_t<Is>...>;
template<class Indexes>
struct var_enum_over;
template<class Indexes>
using var_enum_over_t=typename var_enum_over<Indexes>::type;
template<class T,T...ts>
struct var_enum_over<std::integral_sequence<T,Is...>>{
using type=var_enum_t<Is...>;
};
template<std::size_t N>
using var_index_t=var_enum_over_t<std::make_index_sequence<N>>;

template<std::size_t N>
var_index_t<N> var_index(std::size_t I){
constexpr auto table=[]<std::size_t...Is>(std::index_sequence<Is...>)->std::array<N,var_index_t<N>>{
return { var_index_t<N>(constant_v<Is>)..., };
}(std::make_index_sequence<N>{});
if (I>=N) throw 0; // todo: something better
return table[I];
}

(Probably has typos).

Now you can:

auto idx=var_index<5>(3/* 3 can be runtime */);
std::visit([](auto three){
// three is a compile time value here
}, idx);

Why do I get this error in Clang? constexpr if condition is not a constant expression

Did I do something wrong in the class method's definition?

The problem is that function parameters (like str) are not constant expressions. For example, we cannot use str as a template non-type parameter, or as a size of a built-in array.

This means the expression is_str( str ) that contains the subexpression str is itself not a constant expression, either.

Moreover, the code is rejected by all the three major compilers, if you're using their latest versions. Demo


A simpler way of doing this would be as shown below:

template <typename T>
bool is_escape(const T& str)
{
if constexpr(std::is_convertible_v<T, std::string>)
{

return ! std::string(str).rfind( "\033", 0 );
}
else
{
return false;
}

}

Working demo


Or just return is_escape_v(str):

template <class T>
inline bool is_escape( const T& str ) const
{
if constexpr (std::is_convertible_v<T, std::string_view>)
{
return is_escape_v( str );
}
else
{
return false;
}

}

Demo

How to pass a constexpr as a function parameter c++

You cannot use a function parameter where a compile-expression is expected, because the parameter is not constexpr, even in constexpr function (your constexpr function could also be called with non-constexpr values).

In your case, the easiest solution is probably to use a non-type template parameter:

template <int n_steps>
auto create_step_vectors(double step_size)
{
std::array<double, n_steps + 1> arr;
for (int i = 0; i <= n_steps; i++)
{
arr[i] = i * step_size;
}
return arr;
}

And then

constexpr int n_step{ static_cast<int>(1 / x_step) };
const auto arr = create_step_vectors<n_step>(1.);

Why is comparing two parameters of a constexpr function not a constant condition for static assertion?

A constexpr function can also be invoked with arguments evaluated at run-time (in that case, it just gets executed just like any regular function). See, for instance, this live example.

A static_assert(), on the other hand, strictly requires its condition to be a constant expression that can be evaluated at compile time.

Why isn't the compiler evaluating a constexpr function during compilation when every information is given?

The initial error is that array is not initialized in your constructor. You can fix this by initializing it:

template<typename T>
constexpr List<T>::List()
: length(0)
, array{}
{
}

After that, you will run into the problem that the <algorithm> functions you use are not constexpr; you can fix this by copying the example definitions from the standard into your implementation and marking your copies constexpr:

template<class BidirIt, class OutputIt>
constexpr OutputIt reverse_copy(BidirIt first, BidirIt last, OutputIt d_first)
{
while (first != last) {
*(d_first++) = *(--last);
}
return d_first;
}

// etcetera

Finally, you're using lambdas as filter predicates (in sort); lambdas are illegal in a constant-expression. The fix here is to expand the lambdas by hand into function objects:

template<typename T>
constexpr List<T> List<T>::sort() const
{
if (length == 0)
{
return *this;
}
T pivot = head();
struct Lt { T pivot; constexpr bool operator()(T t) const { return t < pivot; } };
struct Ge { T pivot; constexpr bool operator()(T t) const { return t >= pivot; } };
return tail().filter(Lt{pivot}).sort().add(pivot)
.merge(tail().filter(Ge{pivot}).sort());
}

With these changes your code will compile under clang 3.7, though not gcc 5.2.0.


gcc 5.2.0 has two bugs:

First, it doesn't like the combined decrement-indirection *(--last) in reverse_copy; this is easily fixed:

template<class BidirIt, class OutputIt>
constexpr OutputIt reverse_copy(BidirIt first, BidirIt last, OutputIt d_first)
{
while (first != last) {
--last;
*(d_first++) = *last;
}
return d_first;
}

Second, it doesn't like comparing pointers into array; however, it can be satisfied by changing array + length to &array[length].

Live example with these changes (works with clang 3.7 and gcc 5.2.0).



Related Topics



Leave a reply



Submit