10

In C++11 we get constexpr:

constexpr int foo (int x) {
    return x + 1;
}

Is it possible to make invocations of foo with a dynamic value of x a compile time error? That is, I want to create a foo such that one can only pass in constexpr arguments.

Thomas Eding
  • 35,312
  • 13
  • 75
  • 106

4 Answers4

9

Replace it with a metafunction:

template <int x> struct foo { static constexpr int value = x + 1; };

Usage:

foo<12>::value
Kerrek SB
  • 464,522
  • 92
  • 875
  • 1,084
4

Unfortunately, there is no way guarantee that a constexpr function, even the most trivial one, will be evaluated by the compiler unless absolutely necessary. That is, unless it appears in a place where its value is required at compile time, e.g. in a template. In order to enforce the compiler to do the evaluation during compilation, you can do the following:

constexpr int foo_implementation (int x) {
    return x + 1;
}

#define foo(x) std::integral_constant<int, foo_implementation(x)>::value

and then use foo in your code as usual

int f = foo(123);

The nice thing about this approach is that it guarantees compile-time evaluation, and you'll get a compilation error if you pass a run-time variable to foo:

int a = 2;
int f = foo(a); /* Error: invalid template argument for 'std::integral_constant',
                   expected compile-time constant expression */

The not so nice thing is that it requires a macro, but this seems currently inevitable if you want both guaranteed compile-time evaluation and pretty code. (I'd love to be proven wrong though!)

Iliyan Georgiev
  • 153
  • 1
  • 7
2

Yes, it can now be done in purely idiomatic C++, since C++20 added support for this kind of problem. You annotate a function with consteval and can be sure that it's being evaluated during compile time. https://en.cppreference.com/w/cpp/language/consteval

consteval int foo( int x ) {
    return x + 1;
}

int main( int argc, char *argv[] )
{
    return foo( argc ); // This will not compile
    return foo( 1 );    // This works
}

Also see this godbolt.org demo in the 3 most relevant compilers.

JanSordid
  • 59
  • 6
1

I would use static_assert as shown in this example

#include<iostream>

constexpr int foo(int x) {
        return x+1;
}

int main() {
        // Works since its static
        std::cout << foo(2) << std::endl;
        static_assert(foo(2) || foo(2) == 0, "Not Static");

        // Throws an compile error
        int in = 3;
        std::cout << foo(in) << std::endl;
        static_assert(foo(in) || foo(in) == 0, "Not Static");
}

For more infos: http://en.cppreference.com/w/cpp/language/static_assert

magu_
  • 4,766
  • 3
  • 45
  • 79
  • I don't have a compiler ATM with `constexpr`, but wouldn't this idea extend to putting the `static_assert` directly into `foo`? E.g. `constexpr int foo(int x) { static_assert(x == x, "Not Static"); return x+1; }` – Thomas Eding Sep 18 '14 at 17:44
  • 1
    @ThomasEding. You could but then you would loose the flexibility of using the same function as run-time-evaluated function. Since `static_assert` checked at compile time it will also not affect runtime. – magu_ Sep 19 '14 at 06:01