2

Is it possible to store a string in a constexpr struct:

So far I could only come up with:

struct A
{
    constexpr A(std::string_view n): m_name(n) {}   
    constexpr auto name(){ return m_name; }

    std::string_view m_name; // This might become dangling!!
} 


which is cleary only a good idea if this class is only used like this

A a = {"Hello"};
constexpr A b = {"World"};

and not like this

auto makeA(std::string n) { return A{n}; }
A a = makeA("Hello"); // Dangling internal std::string_view

I need the constexpr to construct the struct at compile time. Is it possible to make this safer at run-time, because with std::string_view, its not.

Jarod42
  • 203,559
  • 14
  • 181
  • 302
Gabriel
  • 8,990
  • 6
  • 57
  • 101
  • Take a *"char_sequence"*, Something like [operator ""_cs](https://stackoverflow.com/questions/58866964/ensure-that-char-pointers-always-point-to-the-same-string-literal/58868005#58868005) might help. – Jarod42 Dec 31 '19 at 10:35
  • 1
    What about making 2 similar types. One with `std::string_view` and one with `std::string`? Use the `std::string_view` type at compile time and the `std::string` type else. Maybe add an implicit conversion from compile time to run time type. – Timo Dec 31 '19 at 13:56
  • to use the constexpr you need to have in mind the very end value of the constexpr element need to be know by compiler, otherwise it can't be a const. – TheArchitect Dec 31 '19 at 15:52

2 Answers2

2

This is not really a question of safety as much as it is a question of semantics. Nothing prevents you from doing the exact same thing at compiletime:

constexpr A blub()
{
    char str[] = "asdf";
    return { str };
}

Since there is no way to ever call this function in a core constant expression, a program that contains code like this is ill-formed, no diagnostic required [dcl.constexpr]/5, which really isn't any better than invoking undefined behavior at runtime…

Compiletime or not, you have to ask yourself the question: Should this struct own a string or refer to an existing string? I would strongly recommend against making your struct own a string in a runtime context and refer to an existing string in a compiletime context, even if you find a way to pull this off in theory. We're talking about completely different semantics here. Completely different semantics should generally better be modeled by different types rather than one type that completeley changes its meaning depending on context…

If you want to make a constexpr struct own a string, you'll currently have to resort to some constexpr string implementation such as, e.g., this one. Since your question is tagged with , note that std::string will be useable in a constexpr context starting with C++20 [basic.string]. So, in C++20, you will be able to just have the member be an std::string

Michael Kenzel
  • 15,508
  • 2
  • 30
  • 39
  • 2
    "*note that std::string will be useable in a constexpr context starting with C++20*" Except that such strings cannot survive outside of a `constexpr` context. – Nicol Bolas Dec 31 '19 at 14:26
1

You might do:

template<typename Char, Char... Cs>
struct CharSeq
{
    static constexpr const Char s[] = {Cs..., 0}; // The unique address
};

// That template uses the extension
template<typename Char, Char... Cs>
constexpr CharSeq<Char, Cs...> operator"" _cs() {
    return {};
}

See my answer from String-interning at compiletime for profiling to have MAKE_STRING macro if you cannot used the extension (Really more verbose, and hard coded limit for accepted string length).

Then

struct A
{
    template <char ... Cs> 
    constexpr A(CharSeq<char, Cs...>) : m_name(CharSeq<char, Cs...>::s) {}

    constexpr auto name(){ return m_name; }

    std::string_view m_name;
};

With only valid usages similar to:

A a = {"Hello"_cs};
constexpr A b = {"World"_cs};
Jarod42
  • 203,559
  • 14
  • 181
  • 302