Considering 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 come the hash0
(where str0
is created by value) gets evaluated at runtime while hash1
(where str1
is created by reference) get evaluated at compile time?
I've tried it in both MSVC and GCC 6.2/7.0 and it seems they both fail. Is it something in the standard which prevents it to be 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 force this to evaluate at compile time with a constexpr function:
auto func1(std::unordered_set<PrehashedString>& iset) {
return iset.count("my_key");
}
Note:
I am aware how to achieve this by making the constructor only accept string literals (template <size_t N> PrehashedString(const char (&istring)[N]){...})
, and utilizing the string literal length as a template parameter for the hash function, I'm just curious in why it fails.
hash0
at compile time but nothash1
Demo – Godivaconstexpr
gets much less annoying and restricted in c++14, you can write more natural code. Second, I doubtunordered_set
can beconstexpr
as it does memory allocation? Andnew
is notconstexpr
? – Decrement