frozenca
frozenca

Reputation: 877

static_assert compile-time argument check with C++20 concept

I'm coding a function that evaluates binomial coefficients: binom(N, K)

I want to check that both N and K are unsigned-type integers with N >= K at compile time, but having a problem with this.

Here's my code:

template <typename N>
concept Unsigned = std::is_unsigned_v<N>;

template <Unsigned U>
constexpr double binom(U N, U K) {
    static_assert(N >= K);
    double d = 1.0;
    while (K) {
        d *= static_cast<double>(N) / static_cast<double>(K);
        K--;
        N--;
    }
    return d;
}

template <Unsigned U>
constexpr double binom_pmf(U N, U K, double theta) {
    return binom(N, K) * std::pow(theta, K) * std::pow(1 - theta, N - K);
}

Here is the error message from Clang 10:


/mnt/c/programming/ML/2_3_1_binomial_bernoulli.cpp:12:19: error: static_assert expression is not an integral constant expression
    static_assert(N >= K);
                  ^~~~~~
/mnt/c/programming/ML/2_3_1_binomial_bernoulli.cpp:24:12: note: in instantiation of function template specialization 'binom<unsigned long>' requested here
    return binom(N, K) * std::pow(theta, K) * std::pow(1 - theta, N - K);
           ^
/mnt/c/programming/ML/2_3_1_binomial_bernoulli.cpp:36:16: note: in instantiation of function template specialization 'binom_pmf<unsigned long>' requested here
        y[K] = binom_pmf(N, K, theta);

The problem is static_assert(N >= K).

I've declared parameter N as constexpr size_t, and K is the loop parameter in for (size_t K = 0; K <= N; K++), so anyone can conclude that N and K are surely unsigned integers with N >= K, but the compiler doesn't look so happy.

Oh, the compiler is also complaining when I try to insert static_assert(theta >= 0.0 && theta <= 1.0); in binom_pdf.

What should I do? Thanks in advance.

Upvotes: 0

Views: 1221

Answers (2)

Davis Herring
Davis Herring

Reputation: 40023

If you really are happy with all these being available only for constant expressions, declare the functions consteval and throw (no actual exception needed) when you don’t like the arguments. That allows floating-point types even without compiler support for them as template parameters.

Upvotes: 0

Jarod42
Jarod42

Reputation: 218148

Function parameters are not constexpr.

template <Unsigned U, U N, U K> constexpr double binom() would allow your static_assert.

Upvotes: 1

Related Questions