Given the following code:
#include <unordered_set> #include <type_traits> #include <cstring> constexpr auto cstring_hash(const char* istring) -> size_t { return (*istring) == '\0' ? size_t(0): (*istring) + cstring_hash(istring + 1); } constexpr auto cstring_length(const char* istring) -> size_t { return (*istring) == '\0' ? size_t(0): size_t(1) + cstring_length(istring + 1); } class PrehashedString { public: constexpr PrehashedString(const char* istring) : str_(istring) , hash_(cstring_hash(istring)) , size_(cstring_length(istring)) { } constexpr auto& get_hash() const { return hash_; } auto operator==(const PrehashedString& iother) const { return size_ == iother.size_ && std::strcmp(str_, iother.str_) == 0; } private: const char* str_; size_t hash_; size_t size_; }; namespace std { template <> struct hash<PrehashedString> { constexpr auto operator()(const PrehashedString& ihashed_string) const -> size_t { return ihashed_string.get_hash(); } }; }
How is it that hash0 (where str0 is created by value) is evaluated at runtime, and hash1 (where str1 is created by reference) is evaluated at compile time? I have tried this in both MSVC and GCC 6.2 / 7.0, and it seems that both of them fail. Is this something in the standard that prevents it from being evaluated at compile time?
auto func0() { const auto str0 = PrehashedString("my_string_0"); const auto hash0 = str0.get_hash(); const auto& str1 = PrehashedString("my_string_1"); const auto hash1 = str1.get_hash(); return hash0 + hash1; }
Expanding it further, how can I make it evaluate at compile time using the constexpr function:
auto func1(std::unordered_set<PrehashedString>& iset) { return iset.count("my_key"); }
Note: I know how to do this if the constructor accepts only string literals (template <size_t N> PrehashedString(const char (&istring)[N]){...}) and uses string literal length as a template parameter for the hash function I'm just wondering why this is failing.
source share