17

Is there a way to achieve different behaviour of a constexpr function in the compilation phase and at runtime?

Consider the following example (using a theoretical feature from D: static if):

constexpr int pow( int base , int exp ) noexcept
{
    static if( std::evaluated_during_translation() ) {
        auto result = 1;
        for( int i = 0 ; i < exp ; i++ )
            result *= base;
        return result;
    } else { // std::evaluated_during_runtime()
        return std::pow( base , exp );
    }
}

If not, is there a way to restrict constexpr to be compile-time only?

nonsensation
  • 3,627
  • 6
  • 28
  • 41
  • 2
    If you use it in a [conext where a constant expression is required then it should be evaluated at compile time](http://stackoverflow.com/a/25891133/1708801) – Shafik Yaghmour Feb 23 '15 at 20:56
  • You might have luck with GCC-intrinsic `__builtin_constant_p`... – Deduplicator Feb 23 '15 at 21:09
  • @Deduplicator not a very well documented feature, at least last time I looked but I put everything I could find on it in my [answer here](http://stackoverflow.com/a/24400015/1708801). – Shafik Yaghmour Feb 23 '15 at 21:24
  • Found this: http://stackoverflow.com/questions/8936549/constexpr-overloading is there any movement on constexpr overloading going on? – nonsensation Feb 23 '15 at 21:26
  • I believe that std::pow will become constexpr before the rules for constexpr are changed to allow what you are asking for. – Marc Glisse Feb 23 '15 at 22:24
  • `std::pow` was just an example, there might be other usecases where compiletime and runtime algorithms might differ – nonsensation Feb 23 '15 at 23:10
  • 5
    [this](http://coliru.stacked-crooked.com/a/24785fe2f3eb6798) (*proof-of-concept*) that I just wrote might be of interest, accepted by *gcc* and *msvc*, though *clang* has a [bug](http://llvm.org/bugs/show_bug.cgi?id=15481) related to the magic used; let me know and I'll provide it as an answer to your question. – Filip Roséen - refp Feb 23 '15 at 23:39
  • 1
    @FilipRoséen-refp so `noexcept( constexpr-func )` can determine if something is evaluates during translation/runtime - nice! It took me a while. An answer would be great since you already did alot of work – nonsensation Feb 23 '15 at 23:59
  • @Serthy `noexcept(expr)` will yield `true` if *expr* is a *constant-expression*, so the "magic" involved is simply checking whether the arguments involved can appear in a *constant-expression* - and if so we will call the `constexpr` version of our function. – Filip Roséen - refp Feb 24 '15 at 00:06
  • @FilipRoséen-refp that is a neat trick, let me know if you provide an answer based on that. – Shafik Yaghmour Feb 24 '15 at 03:50
  • 1
    This post seems like a good idea but I'm thinking about whether it can be adopted for functions with parameters... https://stackoverflow.com/a/55290363/13080413 or in C++17, don't declare you function `noexcept`, and then do `noexcept(func(arg))` -- it'll be true if a constexpr function NOT marked noexcept was executed at compile time – Sean Aug 21 '20 at 06:57

2 Answers2

7

No, there is no such way.

Sorry.

N3583 is a paper proposing changes to allow what you are asking for.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • Just saw this [Scott Meyers talk](https://www.youtube.com/watch?v=WDIkqP4JbkE) where he also says that one have to use two different functions. – nonsensation Feb 23 '15 at 21:38
  • This paper is a good answer, unfortunately we have to wait a few years until (if ever) it gets implemented. A type trait would be easy to implement for compiler-writers and would have a minimal impact on the standard. – nonsensation Feb 23 '15 at 21:47
  • Do you know the status of this proposal? – nonsensation Jun 10 '15 at 20:30
  • @Serthy No. I spent a few minutes, and didn't see it. isocpp sometimes links to "current proposal status" lists, you can look there. – Yakk - Adam Nevraumont Jun 11 '15 at 15:39
  • Update: There seem to be different approaches for this now [this blog post](http://saadahmad.ca/detecting-evaluation-context-inside-constexpr-functions/) and [this proposal](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0595r0.html) – nonsensation Jan 04 '18 at 08:22
  • Has this situation changed with C++17? – einpoklum Apr 28 '18 at 14:37
  • @einpok no: google constexpr operator, which is the last time I heard about this. – Yakk - Adam Nevraumont Apr 28 '18 at 16:43
5

Prior to C++20, this wasn't possible. C++20 then added std::is_constant_evaluated which is exactly for this use case:

constexpr int pow(int base, int exp) noexcept
{
    if (std::is_constant_evaluated())
    {
        auto result = 1;

        for (int i = 0; i < exp; i++)
            result *= base;

        return result;
    } 
    else
    {
        return std::pow(base, exp);
    }
}

Note that the if statement itself is not constexpr. If it were, the whole else arm would be removed from the function and it would always run the if arm, no matter if at compile time or runtime. With a normal if statement, you basically get two functions. One that runs at compile time:

constexpr int pow(int base, int exp) noexcept
{
    auto result = 1;

    for (int i = 0; i < exp; i++)
        result *= base;

    return result;
}

and one that gets compiled an runs at runtime:

constexpr int pow(int base, int exp) noexcept
{
    return std::pow(base, exp);
}

The compiler can safely remove the if arm because it can prove that it isn't reachable at runtime. Pretty neat.

Timo
  • 9,269
  • 2
  • 28
  • 58