57

Related: Function returning constexpr does not compile

I feel like constexpr is limited in usefulness in C++11 because of the inability to define two functions that would otherwise have the same signature, but have one be constexpr and the other not constexpr. In other words, it would be very helpful if I could have, for example, a constexpr std::string constructor that takes constexpr arguments only, and a non-constexpr std::string constructor for non-constexpr arguments. Another example would be a theoretically complicated function that could be made more efficient by using state. You can't easily do that with a constexpr function, so you are left with two choices: have a constexpr function that is very slow if you pass in non-constexpr arguments, or give up on constexpr entirely (or write two separate functions, but you may not know which version to call).

My question, therefore, is this:

Is it possible for a standard-compliant C++11 implementation to allow function overloading based on the arguments being constexpr, or would this require updating the standard? If it is not allowed, was it intentionally not allowed?


@NicolBolas: Say I have a function that maps an enum to a std::string. The most straight-forward way to do this, assuming my enum goes from 0 to n - 1, is to create an array of size n filled with the result.

I could create a static constexpr char const * [] and construct a std::string on return (paying the cost of creating a std::string object every time I call the function), or I can create a static std::string const [] and return the value I look up, paying the cost of all of the std::string constructors the first time I call the function. It seems like a better solution would be to create the std::string in memory at compile time (similar to what is done now with char const *), but the only way to do this would be to alert the constructor that it has constexpr arguments.

For a an example other than a std::string constructor, I think it's pretty straight-forward to find an example where, if you could ignore the requirements of constexpr (and thus create a non-constexpr function), you could create a more efficient function. Consider this thread: constexpr question, why do these two different programs run in such a different amount of time with g++?

If I call fib with a constexpr argument, I can't beat do better than the compiler optimizing away the function call entirely. But if I call fib with a non-constexpr argument, I may want to have it call my own version that implements things like memoization (which would require state) so I get run time similar to what would have been my compile time had I passed a constexpr argument.

Community
  • 1
  • 1
David Stone
  • 26,872
  • 14
  • 68
  • 84
  • Are you sure you actually need this? It's perfectly OK to call `constexpr` functions with non-constant arguments. – Kerrek SB Jan 20 '12 at 04:17
  • Do you have something more than "theoretical" possibilities to want this? `std::string` cannot have a constexpr constructor (except for the constructor that doesn't take parameters) because it must allocate storage for the string. Even with small-string optimization, the possibility of allocation exists. Do you have an example of a "theoretically complicated function that could be made more efficient by using state"? – Nicol Bolas Jan 20 '12 at 04:27
  • @NicolBolas: I don't believe that's true, see my answer. – Ben Voigt Jan 20 '12 at 04:32
  • 3
    A quote from [this paper seems relevant to your question](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2235.pdf). `We don’t propose to make constexpr applicable to function arguments because it would be meaningless for non-inline functions (the argument would be a constant, but the function wouldn’t know which) and because it would lead to complications of the overloading rules (can I overload on constexpr-ness? — no).` – Jesse Good Jan 20 '12 at 06:21
  • 1
    I updated my question to respond to Nicol Bolas. It should also answer the concern brought up by Kerrek SB. – David Stone Jan 21 '12 at 05:21
  • Another related issue that I've recently considered is this: it would be nice to combine assert with static_assert. In other words, if the compiler can determine that an assertion would fail (due to, for example, inlining), I would like for it to just let me know at compile-time rather than waiting until I encounter it at run-time. However, if it cannot determine it at compile time, I'd want just a regular assert. It seems like the easiest way to implement this would be something like `if (is_constexpr (value)) static_assert (condition); else assert (condition);` – David Stone Apr 03 '12 at 04:15
  • `if (is_constexpr (value)) static_assert (condition); else assert (condition);` Dead code elimination does not work in such way. – Tomilov Anatoliy May 22 '13 at 08:07
  • @Dukales: yes, I understand, which is why I said "I would like", rather than "I do like". A lot of code would be made much simpler (and code like this possible) if we had `static if` or whatever you want to call it. Of course, even if we had `static if`, I couldn't use `static_assert` because even though I checked that `condition` is a `constexpr`, `static_assert` doesn't know that I did that and it still wouldn't compile. – David Stone May 26 '13 at 00:43
  • 2
    I too would like this. Another example where this would be helpful is bit/population count of a bit field. Many processors include special instructions for this, so if the constexpr function is called with non-constexpr argument I'd like to use the processor instruction. But processor instructions aren't available at compile-time so I need to use another alogrithm at compile-time. – pyrachi Jul 22 '13 at 22:50
  • Here is a partial solution to the problem: http://stackoverflow.com/a/39922472/6846474 –  Oct 07 '16 at 18:12
  • Is there any new solution for this subject since C++20? – Adam Jun 13 '20 at 13:55
  • 3
    @Adam: No. I am working on a paper targeting C++23: https://github.com/davidstone/isocpp/blob/master/constexpr-parameters.md – David Stone Jun 14 '20 at 15:01
  • Ok thanks, for the update! – Adam Jun 14 '20 at 17:08
  • C++20 added `std::is_constant_evaluated()` [see an answer to: Is is_constexpr possible in C++11?](https://stackoverflow.com/questions/13299394/is-is-constexpr-possible-in-c11/67776077#67776077) – Amir Kirsh May 31 '21 at 15:07

9 Answers9

41

I agree that this feature is missing - I need it too. Example:

double pow(double x, int n) {
    // calculate x to the power of n
    return ...
}

static inline double pow (double x, constexpr int n) {
    // a faster implementation is possible when n is a compile time constant
    return ...
}

double myfunction (double a, int b) {
    double x, y;
    x = pow(a, b);  // call version 1 unless b becomes a compile time constant by inlining
    y = pow(a, 5),  // call version 2
    return x + y;
}

Now I have to do this with templates:

template <int n>
static inline double pow (double x) {
    // fast implementation of x ^ n, with n a compile time constant
    return ...
}

This is fine, but I miss the overload opportunity. If I make a library function for others to use then it is inconvenient that the user has to use different function calls depending on whether n is a compile time constant or not, and it may be difficult to predict whether the compiler has reduced n to a compile time constant or not.

McDowell
  • 107,573
  • 31
  • 204
  • 267
A Fog
  • 4,360
  • 1
  • 30
  • 32
  • 12
    This would be a good addition to the standard – Matt Clarkson Apr 03 '13 at 09:49
  • Here's another example: [What is the function parameter equivalent of constexpr?](http://stackoverflow.com/q/39284065) And trying the same with templates (what a mess): [Parameterization and “function template partial specialization is not allowed”](http://stackoverflow.com/q/39314690). – jww Sep 04 '16 at 14:24
10

Edit: Trick described below is not guaranteed to work anymore!

Detecting constexpr can't be made using overloads (like others already replied) but overloads are just one way to do it.

The typical problem is that we can't use something that can improve run-time performance (for example to call non-constexpr functions or to cache results) in constexpr function. So we may end up with two different algorithms, one less efficient but writable as constexpr, other optimized to run fast but not constexpr. Then we want compiler not to choose the constexpr algorithm for run-time values and vice versa.

That can be achieved by detecting constexpr and selecting based on it "manually" and then shortening the interface down with preprocessor macros.

First lets have two functions. In general the functions should reach same result with different algorithms. I choose two algorithms that never give same answers here just to test and to illustrate the idea:

#include <iostream>     // handy for test I/O
#include <type_traits>  // handy for dealing with types

// run-time "foo" is always ultimate answer
int foo_runtime(int)
{
    return 42;
}

// compile-time "foo" is factorial
constexpr int foo_compiletime(int num)
{
      return num > 1 ? foo_compiletime(num - 1) * num : 1;
}

Then we need a way to detect that argument is compile time constant expression. If we don't want to use compiler-specific ways like __builtin_constant_p then there are ways to detect it in standard C++ as well. I'm pretty sure that following trick is invented by Johannes Schaub but I can't find the cite. Very nice and clear trick.

template<typename T> 
constexpr typename std::remove_reference<T>::type makeprval(T && t) 
{
    return t;
}

#define isprvalconstexpr(e) noexcept(makeprval(e))

The noexcept operator is required to work compile-time and so branching based on it will be optimized out by most compilers. So now we can write a "foo" macro that selects the algorithm based on argument's constexprness and to test it:

#define foo(X) (isprvalconstexpr(X)?foo_compiletime(X):foo_runtime(X))

int main(int argc, char *argv[])
{
    int a = 1;
    const int b = 2;
    constexpr int c = 3;
    const int d = argc;

    std::cout << foo(a) << std::endl;
    std::cout << foo(b) << std::endl;
    std::cout << foo(c) << std::endl;
    std::cout << foo(d) << std::endl;
}

Expected output is:

42
2
6
42

On the few compilers that I tried it works like expected.

Öö Tiib
  • 10,809
  • 25
  • 44
  • 1
    This seems to fall apart when you want to be able to use the result itself as a constexpr when applied to a constant though. constexpr int e = foo(c); // fails – Edward Kmett Sep 11 '16 at 12:09
  • @EdwardKMETT It will work on case you can write `foo_runtime()` as `constexpr`. Otherwise, yes, when we need constant expression to be returned then we can't use functions that don't return it. IOW `constexpr int fooB = foo_compiletime(b);` There are no overloading in C++ based on return type. – Öö Tiib Sep 12 '16 at 12:54
  • [C++17 unfortunately closed the door for this trick](https://stackoverflow.com/questions/60586265/noexcept-behavior-of-constexpr-functions). – Amir Kirsh Mar 08 '20 at 12:10
  • @AmirKirsh Strange that they removed it also for -std=C++14 mode. It smells like conspiracy to make constexpr not detectable in any way. – Öö Tiib Mar 09 '20 at 05:09
  • `constexpr` is still detectable for static storage, see: https://stackoverflow.com/a/60714976/2085626 – Amir Kirsh Mar 17 '20 at 01:06
  • That does not help since idea is not to assign to static variables but to select between efficient runtime non-constexpr (compiler intrinsic, inline assembler, trigonomertry) and potentially naive but constexpr calls depending on being in constexpr or non-constexpr context. – Öö Tiib Mar 27 '20 at 13:48
8

While there is no such thing as "constexpr overloading" in C++11, you can still use GCC/Clang __builtin_constant_p intrinsic. Note, that this optimization is not very useful for double pow(double), because both GCC and Clang already can optimize pow for constant integral exponents, but if you write a multiprecision or vector library, then this optimization should work.

Check this example:

#define my_pow(a, b) (__builtin_constant_p(b) ? optimized_pow(a, b) : generic_pow(a, b))

double generic_pow(double a, double b);

__attribute__((always_inline)) inline double optimized_pow(double a, double b) {
    if (b == 0.0) return 1.0;
    if (b == 1.0) return a;
    if (b == 2.0) return a * a;
    if (b == 3.0) return a * a * a;
    if (b == 4.0) return a * a * a * a;

    return generic_pow(a, b);
}

double test(double a, double b) {
    double x = 2.0 + 2.0;
    return my_pow(a, x) + my_pow(a, b);
}

In this example my_pow(a, x) will be expanded to a*a*a*a (thanks to dead code elimination), and my_pow(a, b) will be expanded to direct generic_pow call without any preliminary checks.

Lockal
  • 179
  • 1
  • 4
8

It would have to be overloaded based on the result being constexpr or not, rather than the arguments.

A const std::string could store a pointer to the literal, knowing that it would never be written to (using const_cast to remove const from the std::string would be necessary, and that's already undefined behavior). It'd just be necessary to store a boolean flag to inhibit freeing the buffer during destruction.

But a non-const string, even if initialized from constexpr arguments, requires dynamic allocation, because a writable copy of the argument is required, and therefore a hypothetical constexpr constructor should not be used.


From the standard (section 7.1.6.1 [dcl.type.cv]), modifying any object which was created const is undefined behavior:

Except that any class member declared mutable (7.1.1) can be modified, any attempt to modify a const object during its lifetime (3.8) results in undefined behavior.

Ben Voigt
  • 277,958
  • 43
  • 419
  • 720
  • `const` is not a type; it is a *modifier* for a type. There is no way for your code to know that it is in a true `const std::string` instead of just a `std::string` that just so happens to be `const`-accessed right now. Without that knowledge, without being able to tell the difference, it is simply not possible to implement this optimization. That's why someone wrote an old `boost::const_string` class; because there's no way to implement it without specialized code that can detect that it is always constant. – Nicol Bolas Jan 20 '12 at 04:44
  • @NicolBolas: Correct, but I don't think Ben is saying that his suggestion is possible. It would be interesting to allow the `const` modifier to be applied to constructors. – Potatoswatter Jan 20 '12 at 05:27
  • @NicolBolas: You have that backwards. Code must never use `const_cast` to modify a `const std::string` (via pointer or reference, obviously), unless it has external knowledge that the object was not created `const`. Modifying an object that was created `const` is already undefined behavior, adding this optimization wouldn't break anything. I've quoted that particular section of the standard. – Ben Voigt Jan 20 '12 at 06:30
  • @BenVoigt: That doesn't change my point. There is no way for the *constructor* to know that the object it is creating is a `const` object. Without that, there is no way to store special data that `const` member functions can key off of to know that the object is really a `const` object rather than just an object that is currently being accessed as `const` (passing a value to function that takes a `const&`). A `constexpr` constructor doesn't have to create `const` objects. You're talking about a completely new kind of construct, one which doesn't exist in C++11. – Nicol Bolas Jan 20 '12 at 06:44
  • @NicolBolas: I thought I appropriately covered that point in the first sentence of my answer. But you're still missing something. Because `const` member functions can't tell whether the object is `const`, they have to assume it is and not make modifications. The only issue is the destructor. – Ben Voigt Jan 20 '12 at 15:11
  • @BenVoigt: This isn't about `const` member functions. It's about the constructor. The constructor must put the object in a special state of storing a literal string pointer. To do this, the constructor must be absolutely certain of three things: 1) the constructor is being called at compile time via `constexpr`. 2) the constructor's argument is a string literal. 3) the object being constructed is a `const` object. Just because the constructor is a `constexpr` constructor doesn't mean it makes a `const` object. It is not possible to detect *any* of these in C++11. – Nicol Bolas Jan 21 '12 at 05:34
  • @Nicol: (1) isn't necessary. (2) is possible using generalized literals (needed anyway to get the length). And (3) was the whole gist of my answer. – Ben Voigt Jan 21 '12 at 15:31
  • @BenVoigt: User-defined literals cannot call any constructor that your code could not otherwise call. So if they can call a special constructor of `std::string`, then so can you. And your answer doesn't explain how the `std::string` constructor would know that it is making a `const` object. – Nicol Bolas Jan 21 '12 at 17:49
  • @NicolBolas: Uh, yeah, that's the whole point. Doing this would require overloading based on whether the object being constructed is `const`. C++ doesn't do that. That's my answer :) – Ben Voigt Jan 21 '12 at 17:55
  • The problem with using user-defined literals to "enforce" that you're using a string literal, is that you don't enforce anything. Yes, `"foo"_x` requires a literal, but `operator""_x(a)` doesn't. – R. Martinho Fernandes Apr 23 '12 at 05:50
  • @R.MartinhoFernandes: Perhaps so, but I hardly consider the fact that a user is able to trigger UB by trying sufficiently hard a problem. This is C++, after all. The goal is not to prevent the user from breaking his code, it's to build a pit of success. – Ben Voigt Apr 23 '12 at 12:57
  • @BenVoigt ah, sure, I agree with that: if you go the `operator""_x` route, you do so at your own risk. Just pointing out that UDLs don't enforce anything, except out of convention. – R. Martinho Fernandes Apr 23 '12 at 18:51
6

TL;DR: It is possible in C++20, with std::is_constant_evaluated in the <type_traits> header.

I came across this question with the application that @Öö Tiib mentioned in his answer, in mind: I wanted to use a faster algorithm at runtime, but a slower (and constexpr-friendly) algorithm at compile-time.


Using the example in @Öö Tiib's answer:

#include <iostream>
#include <type_traits>

constexpr int foo(int i) {
    if (std::is_constant_evaluated()) {
        // compile-time branch
        return (i > 1) ? foo(i - 1) * i : 1;
    } else {
        // runtime branch
        return 42;
    }
}

int main(int argc, char* argv[]) {
    int a = foo(1);
    const int b = foo(2);
    constexpr int c = foo(3);
    const int d = foo(argc);

    std::cout << a << std::endl;
    std::cout << b << std::endl;
    std::cout << c << std::endl;
    std::cout << d << std::endl;
}

gives the output

2
2
6
42

Here is a link to the program on godbolt.

Note that foo is indeed a legal constexpr function because, as the standard says, (quote copied from cppreference):

A constexpr function must satisfy the following requirements:

  • there exists at least one set of argument values such that an invocation of the function could be an evaluated subexpression of a core constant expression (for constructors, use in a constant initializer is sufficient).

Note, however, that this program:

#include <iostream>
#include <type_traits>

constexpr int foo(int i) {
    if (std::is_constant_evaluated()) {
        // compile-time branch
        return i > 1 ? foo(i - 1) * i : 1;
    } else {
        // runtime branch
        return 42;
    }
}

int main(int argc, char *argv[]) {
    int a = 1;
    const int b = 2;
    constexpr int c = 3;
    const int d = argc;

    std::cout << foo(a) << std::endl;
    std::cout << foo(b) << std::endl;
    std::cout << foo(c) << std::endl;
    std::cout << foo(d) << std::endl;
}

gives the output

42
42
42
42

(Godbolt link)

I'm not entirely sure why this happens, but I suspect that it is because std::cout's operator<< is not marked constexpr, so all the function calls to foo are happening at runtime. Interestingly, however, in Godbolt's assembly output (for x86-64 GCC 11.2), we can see the 42s inlined. So the function is indeed evaluated at compile-time, just not in the way we would expect at first.

wermos
  • 123
  • 1
  • 4
  • 9
4

The problem, as stated, feels wrong.


A std::string, by construction, owns the memory. If you want a simple reference to an existing buffer, you can use something akin to llvm::StringRef:

class StringRef {
public:
  constexpr StringRef(char const* d, size_t s): data(d), size(s) {}

private:
  char const* data;
  size_t size;
};

Of course, there is the bummer that strlen and all the others C functions are not constexpr. This feels like a defect of the Standard (think about all the maths functions...).


As for state, you can (a bit), as long as you understand how to store it. Remember that loops are equivalent to recursions ? Well, likewise, you can "store" state by passing it as argument to a helper function.

// potentially unsafe (non-limited)
constexpr int length(char const* c) {
  return *c == '\0' ? 0 : 1 + length(c+1);
}

// OR a safer version
constexpr int length_helper(char const* c, unsigned limit) {
  return *c == '\0' or limit <= 0 ? 0 : 1 + length_helper(c+1, limit-1);
}

constexpr int length256(char const* c) { return length_helper(c, 256); }

Of course, this form of this state is somewhat limited (you cannot use complicated constructs) and that is a limitation of constexpr. But it's already a huge leap forward. Going further would mean going deeper into purity (which is hardly possible in C++).

Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
2

Is it possible for a standard-compliant C++11 implementation to allow function overloading based on the arguments being constexpr, or would this require updating the standard? If it is not allowed, was it intentionally not allowed?

If the standard doesn't say you can do something, then allowing someone to do it would be non-standard behavior. And therefore, a compiler that allowed it would be implementing a language extension.

That's not necessarily a bad thing, after all. But it wouldn't be compliant C++11.

We can only guess at the intentions of the standards committee. They may have deliberately not allowed it, or it may have been something of an oversight. The fact is that the standard doesn't overloading is allowed, therefore it isn't.

Nicol Bolas
  • 449,505
  • 63
  • 781
  • 982
  • SFINAE can be used to implement overloading with rules different from the default. Using a function argument as a template parameter in the signature may be a legal way to disable a function for non-constexpr arguments. Or maybe not. I'm not going to look it up because, as Ben explains, the result would be useless anyway. – Potatoswatter Jan 20 '12 at 05:31
1

Another option to detect compile-time compilation using SFINAE: http://coliru.stacked-crooked.com/a/f3a2c11bcccdb5bf

template<typename T>
auto f(const T&)
{
  return 1;
}

constexpr auto f(int)
{
  return 2;
}



////////////////////////////////////////////////////////////////////////
template<typename T, int=f(T{})>
constexpr bool is_f_constexpr_for(int) {return true;}

template<typename...>
constexpr bool is_f_constexpr_for(...) {return false;}



template<typename T>
auto g(const T& t)
{
  if constexpr (is_f_constexpr_for<T>(0))
  {

  }
  else
  {

  }
}
NN_
  • 1,593
  • 1
  • 10
  • 26
1

It is possible to identify whether a given static storage variable is a constant expression, using an approach proposed by Richard Smith based on narrowing conversion rules.

We can assign to an unsigned int a consexpr non-negative int without narrowing:

unsigned int u {std::max(0, -3)}; // compiles, max is constexpr

However, we cannot do the above if we use a variable:

int a = 3;
unsigned int u {std::max(0, a)}; // compilation error, narrowing int to unsigned int

To identify whether a given int reference is const expression, we can test whether it can be assigned to an unsigned int without narrowing with either its positive or negative value. This should be possible for any int which value is known at compile time, i.e. can be considered as a constant expression.

template<const int& p> std::true_type
    is_constexpr_impl(decltype((unsigned int){std::max(-p, p)}));
template<const int& p> std::false_type
    is_constexpr_impl(...);
template<const int& p> using is_constexpr =
    decltype(is_constexpr_impl<p>(0));

Now we can have different implementations for runtime and compile time with the macro approach:

int foo_runtime(int num) {
    return num;
}

constexpr int foo_compiletime(int num) {
      return num + 1;
}

#define foo(X) (is_constexpr<X>()?foo_compiletime(X):foo_runtime(X))

And as said, it will mimic an overload for const expression:

int main() {
    static int a = 3;
    static const int b = 42; // considered constexpr
    static const int c = foo_runtime(42); // not constexpr
    static constexpr int d = 4;
    static constexpr int e = -2;
    static int f = 0;
    static const int g = 0; // considered constexpr

    std::cout << foo(a) << std::endl;
    std::cout << foo(b) << std::endl;
    std::cout << foo(c) << std::endl;
    std::cout << foo(d) << std::endl;
    std::cout << foo(e) << std::endl;
    std::cout << foo(f) << std::endl;
    std::cout << foo(g) << std::endl;
}

Above is nice, though not very useful as it is limited to static storage variables. But it does present overloading based on constexpr.


Another approach to achieve the same, without depending on narrowing conversion, can be:

template<const int& p> std::true_type
    is_constexpr_impl(std::array<int, std::max(p, -p)>);
template<const int& p> std::false_type
    is_constexpr_impl(...);
template<const int& p> using is_constexpr = 
    decltype(is_constexpr_impl<p>(0));

The use of std::array above replaces using simple c-array, which doesn't work well for gcc with this approach.


Or another one - again, without relying on narrowing rules - that also works fine:

template<const int& p, typename T = void>
struct is_constexpr: std::false_type {};

template<const int& p>
struct is_constexpr<p, std::void_t<int[std::max(p,-p)+1]>>: std::true_type {};

Note that if we would try to achieve the same with a more simple approach:

template<typename T>
struct is_constexpr: std::false_type {};

template<typename T>
struct is_constexpr<const T>: std::true_type {};

#define foo(X) (is_constexpr<decltype(X)>()?foo_compiletime(X):foo_runtime(X))

We would not achieve our goal for this line:

static const int c = foo_runtime(42); // const but not constexpr
Amir Kirsh
  • 12,564
  • 41
  • 74
  • Can you explain which version is the "bottom line" here? i.e. what's recommended for use? – einpoklum Apr 14 '20 at 11:45
  • @einpoklum what is your required usage? – Amir Kirsh Apr 15 '20 at 18:11
  • Nothing at the moment, just curious. – einpoklum Apr 15 '20 at 18:34
  • @einpoklum the idea was to present that there can be different approaches checking for `is_constexpr`, i.e. using: (1) narrowing rules, (2) template non-type parameter, (3) array size. Though, all these options work only for static storage variables as they are all based on passing the variable being checked as a template parameter by reference. – Amir Kirsh May 14 '20 at 11:29
  • A simpler implementation than `std::max(x, -x)` that works with all types (and `INT_MIN`) would be `(void(expr), 0)`. It always produces the value `0`, but is only a constant expression if the first argument to the comma operator is a constant expression. – David Stone May 22 '21 at 20:43