What is preventing this constexpr to be evaluated at compile time?
Asked Answered
G

0

6

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.

Ganges answered 7/12, 2016 at 8:37 Comment(6)
Try adding a constexpr copy ctor.Roadstead
I tried clang 3.9.0 and it is able to do everything in compile time.Subfamily
I got reversed result, hash0 at compile time but not hash1 DemoGodiva
I've tried constexpr copy-constructors etc, but it still doesn't help in the unordered_set case.Ganges
Do you really need c++11? constexpr gets much less annoying and restricted in c++14, you can write more natural code. Second, I doubt unordered_set can be constexpr as it does memory allocation? And new is not constexpr?Decrement
@Yakk: As noted at the end, it works with the unordered_set if I construct it with a string literal, and utilize a templated version of cstring_hash taking length as a template parameter. On your first question, I'm just used to C++11 constexpr.Ganges

© 2022 - 2024 — McMap. All rights reserved.