Zebrafish
Zebrafish

Reputation: 13936

Can you call a constexpr function to assign a constexpr value with a forward declaration?

I found myself in a weird spot, with the error message "expression did not evaluate to a constant":

constexpr uint64 createDynamicPipelineStateMask();

static inline constexpr uint64 dynamic_state_mask = createDynamicPipelineStateMask();


struct CullMode
{
    static constexpr inline bool bIsDynamicState = false;
    static constexpr inline uint64 mask = 0;
};

constexpr uint64 createDynamicPipelineStateMask()
{
    uint64 dynamic_mask = 0;
    if constexpr (CullMode::bIsDynamicState) dynamic_mask |= CullMode::mask;

    return dynamic_mask;
}

Now I guess I could just move the constexpr value to below the definition, but I wanted to know the answer to this question. I know that it's compiler, and not the linker that needs the full definition of the constexpr function, but the compiler has it right below.

Upvotes: 0

Views: 216

Answers (1)

user12002570
user12002570

Reputation: 1

I know that it's compiler, and not the linker that needs the full definition of the constexpr function, but the compiler has it right below.

The behavior of the program can be understood using expr.const#5 which states:

5. An expression E is a core constant expression unless the evaluation of E, following the rules of the abstract machine ([intro.execution]), would evaluate one of the following:

5.2 an invocation of an undefined constexpr function;

(emphasis mine)

This means that when writing

//------------vvvvvvvvv--------------------------> note the constexpr here, this is not allowed because the call expression cannot be used in a constexpr context until the function is defined
static inline constexpr int dynamic_state_mask = createDynamicPipelineStateMask();

the call expression createDynamicPipelineStateMask() at this point is an invocation of an undefined constexpr function and so the initializer is not a constant expression according to the above quoted statement which in turn means that it cannot be used as an initializer to initialize dynamic_state_mask. Basically, you can't use the call expression createDynamicPipelineStateMask() in a constexpr context until the function is defined.


But the following is perfectly fine.

//------------v-------------------------->note no constexpr here, this is allowed
static inline  int dynamic_state_mask = createDynamicPipelineStateMask();

This time, even though createDynamicPipelineStateMask() does not evaulate to a constant expression, it can still be used as an initializer to initialize dynamic_state_mask as we have removed the constexpr specifier from the left hand side of the declaration. Basically this isn't a constexpr context and so using the call expression as an initializer is fine.


Perhaps a contrived example might clear this further:

constexpr int func();

//constexpr int i = func();   //not allowed as the call expression func() at this point is an invocation of an undefined constexpr function and so the initializer is not a constant expression

int i = func();               //allowed
constexpr int func()
{
    return 4;
}
constexpr int j = func();     //allowed

Demo Clang&Gcc,

MSVC Demo

Upvotes: 4

Related Questions