13

I'm wondering why std::string can't be constexpr if it's a stack variable:

#include <string>
#include <iostream>
#include <cstring>

constexpr std::string str15 {"123456789012345"}; // This compiles!

struct Foo
{
    constexpr Foo() : a(6) {
        for (int i = 0; i < 8; ++i)
            buff[i] = 0;
    }
    int a;
    char buff[8];
};

constexpr void func()
{
    constexpr Foo f{}; // This compiles
    constexpr std::string str15 {"123456789012345"}; // This doesn't compile
}

int main() {
    func();
    return 0;
}

Error:

'std::string{std::__cxx11::basic_string<char>::_Alloc_hider{((char*)(& str15.std::__cxx11::basic_string<char>::<anonymous>.std::__cxx11::basic_string<char>::<unnamed union>::_M_local_buf))}, 15, std::__cxx11::basic_string<char>::<unnamed union>{char [16]{'1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', 0}}}' is not a constant expression

I get the stack variable and the static variable are different places in memory, but the compiler will accept arbitrary objects as local variables as constexpr. My Foo class doesn't have a const char* as argument, so that's a difference, but it's not as if const char* constructor can't be used in constexpr, because higher above:

constexpr std::string str15 {"123456789012345"}; // This compiles

https://godbolt.org/z/cffPq6nns

14
  • 6
    clang gives a nice error for why: godbolt.org/z/71PjMcYaW Commented yesterday
  • 1
    pretty sure it's because of SSO Commented yesterday
  • 1
    C++ doesn't allow block-scope constexpr variables with non-trivial destructors. Foo has a trivial destructor (it's implicitly generated and does nothing), hence it's gets compile. Regarding global scope string , compiler knows that it can call it's destruction at program termination level. In your non compile line if you mark it "static" then it will gets compile. Commented yesterday
  • 1
    I can't even understand how constexpr can be not static in the first place! A compile time constant must have static storage(maybe in the code segment). Why can it even treated like automatic objects? Commented yesterday
  • 2
    Note clang gives more user friendly error message: godbolt.org/z/3nsT5oh5r - MSVC is happy. Commented yesterday

2 Answers 2

13

The difference between your class Foo and std::basic_string boils down to libstdc++ implementation of SSO, where the latter has a member of pointer type, which uses the underlying data array as initializer.

This in turn violates possible values a pointer can take in a constant expression, when the enclosing object doesn't have static storage duration. From cppreference.com:

If the value is of pointer type, it is one of the following values:

  • the address of an object with static storage duration
  • the address past the end of an object with static storage duration
  • the address of a (non-immediate(since C++20)) function
  • a null pointer value

If Foo had such a pointer, it would suffer from the same problem:

struct Foo
{
    char buff[8];
    char* ptr = buff;
};

int main() {
    constexpr Foo f{}; // Doesn't compile
    return 0;
}
Sign up to request clarification or add additional context in comments.

5 Comments

Could you please explain why MSVC accepts OP's example? Is it because of different std::string implementation in Microsoft's STL? gcc.godbolt.org/z/d3W8rohcY
Correct. Both, MSVC and libc++ implementations avoid using the member pointer type, thus they compile automatic storage constexpr SSO variable without issues.
According to cppreference, "Evaluating a string literal results in a string literal object with static storage duration." However, it also says, "whether successive evaluations of a string literal yield the same or a different object is unspecified." Thus, it seems that although even a local "123456789012345" is static, it is not guaranteed to be a unique instance on each call, so its address could be different each time.
This is about literal, not the std::string object. As rule of thumb - containers in STL (almost) always copy content of the input provided in constructor, so it doesn't really matter where input data comes from in this case, as long as it's acceptable in a constant expression.
3

It's usually best practice for constexpr variables inside functions to also be declared as static. This avoids the possibility that the compiler will decide to extra work each time you call the function. This will also resolve your compile error, while keeping the definitions inside of the function.

#include <string>
#include <iostream>
#include <cstring>

constexpr std::string str15 {"123456789012345"}; // This compiles!

struct Foo
{
    constexpr Foo() : a(6) {
        for (int i = 0; i < 8; ++i)
            buff[i] = 0;
    }
    int a;
    char buff[8];
};

constexpr void func()
{
    static constexpr Foo f{}; // This compiles
    static constexpr std::string str15 {"123456789012345"}; // Complies when static
}

int main() {
    func();
    return 0;
}

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.