Search code examples
c++stringglobal-variablesinlineconstexpr

Why does std::is_literal_type<std::String> == false and how can I get around it?


I'm currently trying to use constexpr to define some input identification stuff:

struct RangeParams {
    string legacyId;
    string fullname;
    string shortname;
    float min = -1; 
    float baseline = 0;
    float max = 1;
    float defaultValue = 0;
};

...

inline constexpr RangeParams curve1 = { "some_id", "my_name", ...};

Unfortunately, I get an error for the constexpr line saying

Constexpr variable cannot have non-literal type 'const RangeParams'

So, I dug into it to figure out what part of this is non-literal, and string was the culprit.

std::cout << std::is_literal_type<float>::value;  // output: 1
std::cout << std::is_literal_type<string>::value;  // output: 0

Finding this has highlighted an important fact for me which is that my understanding of literal-types is rather flawed. For the most part, I'd simply thought of them as being the basics (numbers, strings, booleans, structs made of those things).

So why would a simple string not be a literal type? What's the gotcha here?

Also, how can I get around this? I'm trying to make a global out of my RangeParams and the most modern answer to this question (Defining global constant in C++) appears not be working.


Solution

  • std::string is not a literal. const char[] is. The gotcha is that a std::string is a container of a dynamic size. No, you can't get around this and still use std::string. std::string is unusable in constexpr context.

    You might want to replace std::string with const char* in your code:

    struct RangeParams {
        const char* legacyId;
        const char* fullname;
        const char* shortname;
        float min = -1; 
        float baseline = 0;
        float max = 1;
        float defaultValue = 0;
    };
    

    It's not exactly the same, but given the lack of context, it's hard to justify what did you exactly need the std::string for, thus it's uncertain that a const char* will be a suitable alternative.

    EDIT: A short example should help to understand the misconception. What is the type of 0?. It's an int. What's the type of 'a'? It's a char. What's the type of "abc"? It's not std::string, it's const char[4]!