2

I have read the std::is_constant_evaluated() definition, but I still not sure why (1) is not working with the latest GCC: error: 'x' is not a constant expression

template<auto v>
struct s
{};

constexpr void f(int x)
{
    if (std::is_constant_evaluated())
    {
        // constexpr int z=x; (1)
        // s<x> a; (2)
    }
}

int main(int argc, char* argv[])
{
    f(4);
    //f(argc);
    return 0;
}
  1. By the standard, should that work?
  2. Or just the GCC implementation is buggy?
  3. Somehow can I achieve the expected behavior? Which is basically:

With branching on std::is_constant_evaluated()

  • if it is true: the code can use variables as constexpr (like (2))
  • if it is false: the code use variables as non-constexpr

UPDATE

Can I 'transport' the constexpr-essiveness information into a function? Basically to decide in f() that it was call with constexpr x or not.

UPDATE A more complex example about what I would like to achieve: this sample should stringify the parameter in compile time if possible.

template<auto v>
struct dummy_stringify
{
    static constexpr auto str=v==4 ? "4" : "31"; // this is just an example; imagine here a much more complex logic
};

constexpr void f(int x)
{
    if (std::is_constant_evaluated())
    {
        std::puts("A compile time calculation:");
        //std::puts(dummy_stringify<x>::str);
    } else
    {
        std::cout<<"A runtime calculation:"<<std::endl;
        std::cout<<x<<std::endl;
    }
}
int main(int argc, char* argv[])
{
    f(4);
    f(argc);
    return 0;
}
Balázs Árva
  • 246
  • 5
  • 15

3 Answers3

7

x is not a constant expression, no matter how f itself is evaluated. That's a regular if right there (how is_constant_evaluated is meant to be used). It's not a discarded branch, so it has to contain well-formed code even when f is not constant evaluated. When x won't be a constant expression the function will still contain that (unexecuted) branch, and it will attempt to use x where a constant expression is required. That's plain ill-formed.

GCC is very much correct not to accept it.

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
3

The fundamental issue here is that, even with a constexpr (or even consteval) function being called during constant evaluation (and under an is_constant_evaluated check), there is still only one function shared among all argument values. You therefore can’t ever use a function parameter as a constant expression (even if the call with that parameter is a constant expression). If you want a constant-expression parameter, it has to be a template parameter.

Davis Herring
  • 36,443
  • 4
  • 48
  • 76
0

UPDATE

I have found a solution with a little helper class (of course one can use std::integral_constant)

template<auto val_>
struct val
{
    static constexpr auto value=val_;
};

template<auto v>
struct dummy_stringify
{
    static constexpr auto str=v==4 ? "4" : "31"; // this is just an example; imagine here a much more complex logic
};

#include <iostream>
using namespace std;

template<class T>
constexpr void f(T x)
{
    if constexpr(requires{ T::value; })
    {
        std::puts("A compile time calculation:");
        std::puts(dummy_stringify<T::value>::str);
    } else
    {
        std::cout<<"A runtime calculation:"<<std::endl;
        std::cout<<x<<std::endl;
    }
}
int main(int argc, char* argv[])
{
    f(val<4>{});
    f(argc);
    return 0;
}

It can be improved with a template<char...> auto operator""_v(); to f(4_v)

Balázs Árva
  • 246
  • 5
  • 15