Home > OS >  Would it be sufficient for constexpr, consteval, and constinit to be definitions instead of keywords
Would it be sufficient for constexpr, consteval, and constinit to be definitions instead of keywords

Time:11-09

It seems that the rules for the compile-time keywords: constexpr, consteval and constinit are defined well enough for compilers to warn if you misapply the label.

It would make sense that (much like inline) the compiler can, in all places, apply rules to determine if, in fact, code could have one of the compile-time keywords applied and, be forced, per language specification, to compile as much as possible as if the compile-time keywords had been applied.

Or, at a minimum, if a compile-time keyword is applied to a function and the code would have qualified with had the correct compile-time keywords been applied. Compile that function, as if all the functions called had the correct compile-time keywords.

Here is a simple example:

#include <tuple>

consteval std::tuple<int, int> init1(int x, int y)
{
    return {x,y};
}

std::tuple<int, int>& foo1()
{
    static constinit std::tuple<int, int> x=init1(1,2);
    return x;
}

std::tuple<int, int> init2(int x, int y)
{
    return {x,y};
}

std::tuple<int, int>& foo2()
{
    static std::tuple<int, int> x=init2(1,2);
    return x;
}

static std::tuple<int, int> x3=init2(1,2);

std::tuple<int, int>& foo3()
{
    return x3;
}

see it here: https://godbolt.org/z/KrzGfnEo7

Note that init1/foo1 are fully specified with compile-time keywords and there is no need for a guard variable, since it is initialized completely (not just 0 filled).

The question is about why the compiler doesn't do the same in the cases of init2/foo2 or x3/foo3? Or more precisely why the compiler is allowed NOT to initialize fully at compile time.

EDIT (as per comment) see (Why do we need to mark functions as constexpr?) constexpr would be required. I am concerned more with cases of consteval and constinit. Could the specification be modified to require trying to resolve code at compile-time and not failing because of the absence of constexpr? This would allow interface contracts to just use constexpr as per the related question.

CodePudding user response:

Or, at a minimum, if a compile-time keyword is applied to a function and the code would have qualified with had the correct compile-time keywords been applied.

The basis of your question is the assumption that these keywords are just variations on a theme, that a function which could have some of them ought to have a specific one based on the properties within the function alone.

That's not reasonable.

For functions, any function which could be constexpr could also be consteval. The difference is that one can be called during constant expression evaluation, and the other must be called only during constant expression evaluation. This is not an intrinsic property of the function definition; it is about how the function is to be used.

The compiler should not override the decision of a programmer to be able to use a function at runtime just because the function definition just so happens to also be legit for consteval.

It should also be noted that there is the requirements of a function definition for a consteval function are the same as for a constexpr function.

It is also not possible to know if a function definition ought to be constexpr or not. Remember: while a constexpr function explicitly forbids certain constructs from appearing in the definition, it also permits you to have certain constructs in the definition that aren't allowed to be evaluated during constant evaluation... so long as you never actually allow those code paths to be evaluated during constant evaluation. So there are many function definitions that just so happen to be valid for constexpr even if the programmer didn't intend for them to be evaluated at compile-time.

Lambdas get implicit constexpr definitions only because space in a lambda expression is at a premium.

For variables, implicit constexpr makes even less sense. A variable ought to be constexpr if you intend to use it in a constant expression later on. This requires it to have an initializer that is a constant expression. Implicit constexpr is a problem because you might start relying on the implicit constexpr rule, then change the initializer to no longer be a constant expression and get a strange error in the place where you used its constexpr properties.

It's better to make the user be explicit up-front than to misjudge what the user was trying to do and make a user think something is supposed to be valid when it wasn't intended.

And constinit is solely about ensuring that a user never accidentally uses a non-constant expression to initialize the variable. Making it implicit would make absolutely no sense, as changing the expression to no longer be a constant expression wouldn't cause compilation to fail. Compilers are smart enough to know when a variable is initialized with a constant expression and can decide how to handle that on its own.

  • Related