Search code examples
c++constexprarmclang

Constexpr function in constexpr constructor initialiser list


I would like to initialise a struct member with a hash of the struct name.

constexpr uint32_t myHash(const char* const data)
{ //Some code for hash
    return myHash;
}

struct My_Struct{
    constexpr Test() : ID(myHash("My_Struct"))
    {
    }
    const uint32_t ID; 
}

When I have:

constexpr My_Struct my_constexpr_struct;

Then the hash is computed at compile time successfully. However, when I have in my main function

My_Struct my_normal_struct;

then it will call the

constexpr uint32_t myHash(const char* const data)

function in the code instead of simply initialising the struct member with a compile time constant.

This would obviously incur a significant performance penalty that is avoidable.

Any thoughts or suggestions on how to have the compiler perform this at compile time? I don't really want to do:

constexpr uint32_t MY_STRUCT_ID = myHash("My_Struct");
struct My_Struct{
    constexpr Test() : ID(MY_STRUCT_ID)
    {
    }
    const uint32_t ID; 

Thanks.


Solution

  • constexpr is a request, not a requirement. As such, if you initialize an object outside of a constant expression context, even through a constexpr constructor, there is no guarantee that the initialization will be done at compile time.

    If you want to guarantee compile-time evaluation, you have to call the constexpr function it in a constant expression context. If the explicit use of a variable offends you in some way, you could always force constexpr evaluation through the use of a template:

    template<typename T, T t>
    struct repeat
    {
      using value_type = T;
      static constexpr T value = t;
      constexpr T operator()() const noexcept {return t;}
    };
    
    struct My_Struct{
        constexpr My_Struct() : ID(repeat<uint32_t, myHash("My_Struct")>::value)
        {
        }
        const uint32_t ID; 
    };