I am making a string class whose construction relies on its hash value:
template <size_t N>
static inline constexpr uint64_t hash(const char(&str)[N])
{
//...
}
class String()
{
// Wrong
template <size_t N>
String(const char(&str)[N]) { hash(str)... }
// Right
template <uint64_t hash, size_t N>
String(const char(&str)[N]) { hash...}
}
In the wrong version, if I use "hash" as constexpr, the "str" is out of its life period. But if not, the constexpr function is meaningless. Maybe I have to treat hash value as template arguments(?)
In that case, I have to use it like:
// Simple but wrong
String str("Hello World");
// Right but complex
String<hash("Hello World")> str("Hello World");
// The worst
class Test
{
public:
Test()
: name("Hello World") // ???
{}
private:
String name;
}
Macros work, but it is also hard to use String as member variables.
Is there any better design?
>Solution :
You can pass the string literal as a template parameter once using a helper class:
template<size_t N>
struct literal_string {
constexpr literal_string(const char(& arr)[N]) {
std::ranges::copy(arr, data);
}
constexpr operator std::string_view() const noexcept { return { data, N }; };
char data[N];
};
template<literal_string S>
struct String {
static constexpr uint64_t hash_value = hash(S.data);
constexpr String() {
// You have access to both S.data and hash_value here
}
};
String<"Hello World"> str;