gpt4 book ai didi

c++ - 在 C++ 中,为什么要重载 `const char array` 上的函数和包装 `const char*` 的私有(private)结构?

转载 作者:搜寻专家 更新时间:2023-10-31 00:50:58 25 4
gpt4 key购买 nike

我最近在 ENTT 图书馆遇到了一门有趣的类(class)。此类用于计算字符串的哈希值,如下所示:

std::uint32_t hashVal = hashed_string::to_value("ABC");

hashed_string hs{"ABC"};
std::uint32_t hashVal2 = hs.value();

在查看此类的实现时,我注意到没有构造函数或 hashed_string::to_value成员函数采用 const char*直接地。相反,他们采用一个名为 const_wrapper 的简单结构。 .下面是类实现的简化 View 来说明这一点:

/*
A hashed string is a compile-time tool that allows users to use
human-readable identifers in the codebase while using their numeric
counterparts at runtime
*/
class hashed_string
{
private:

struct const_wrapper
{
// non-explicit constructor on purpose
constexpr const_wrapper(const char *curr) noexcept: str{curr} {}
const char *str;
};

inline static constexpr std::uint32_t calculateHash(const char* curr) noexcept
{
// ...
}

public:

/*
Returns directly the numeric representation of a string.
Forcing template resolution avoids implicit conversions. An
human-readable identifier can be anything but a plain, old bunch of
characters.
Example of use:
const auto value = hashed_string::to_value("my.png");
*/
template<std::size_t N>
inline static constexpr std::uint32_t to_value(const char (&str)[N]) noexcept
{
return calculateHash(str);
}

/*
Returns directly the numeric representation of a string.
wrapper parameter helps achieving the purpose by relying on overloading.
*/
inline static std::uint32_t to_value(const_wrapper wrapper) noexcept
{
return calculateHash(wrapper.str);
}

/*
Constructs a hashed string from an array of const chars.
Forcing template resolution avoids implicit conversions. An
human-readable identifier can be anything but a plain, old bunch of
characters.
Example of use:
hashed_string hs{"my.png"};
*/
template<std::size_t N>
constexpr hashed_string(const char (&curr)[N]) noexcept
: str{curr}, hash{calculateHash(curr)}
{}

/*
Explicit constructor on purpose to avoid constructing a hashed
string directly from a `const char *`.
wrapper parameter helps achieving the purpose by relying on overloading.
*/
explicit constexpr hashed_string(const_wrapper wrapper) noexcept
: str{wrapper.str}, hash{calculateHash(wrapper.str)}
{}

//...

private:
const char *str;
std::uint32_t hash;
};

很遗憾,我看不出 const_wrapper 的用途结构。它是否与顶部的注释有关,该注释指出“散列字符串是编译时工具...”?

我也不确定出现在模板函数上方的注释是什么意思,即“强制模板解析避免隐式转换”。谁能解释一下?

最后,有趣的是要注意这个类是如何被另一个维护 std::unordered_map 的类使用的。以下类型:std::unordered_map<hashed_string, Resource>

这个其他类提供了一个成员函数,可以使用像键这样的字符串向 map 添加资源。其实现的简化 View 如下所示:

bool addResource(hashed_string id, Resource res)
{
// ...
resourceMap[id] = res;
// ...
}

我的问题是:使用 hashed_strings 作为映射的键而不是 std::strings 有什么好处?使用像 hashed_strings 这样的数字类型会更有效吗?

感谢您提供任何信息。学习这门课让我学到了很多东西。

最佳答案

作者试图帮助您避免重复散列字符串时发生的意外性能问题。由于散列字符串很昂贵,您可能想做一次并将其缓存在某个地方。如果它们具有隐式构造函数,您可以在不知道或无意这样做的情况下重复散列相同的字符串。

因此库为字符串文字提供了隐式结构,可以在编译时通过constexpr计算,但显式结构为const char* 通常,因为这些通常不能在编译时完成,您希望避免重复或意外地这样做。

考虑:

void consume( hashed_string );

int main()
{
const char* const s = "abc";
const auto hs1 = hashed_string{"my.png"}; // Ok - explicit, compile-time hashing
const auto hs2 = hashed_string{s}; // Ok - explicit, runtime hashing

consume( hs1 ); // Ok - cached value - no hashing required
consume( hs2 ); // Ok - cached value - no hashing required

consume( "my.png" ); // Ok - implicit, compile-time hashing
consume( s ); // Error! Implicit, runtime hashing disallowed!
// Potential hidden inefficiency, so library disallows it.
}

如果我删除最后一行,您可以在 C++ Insights 看到编译器如何为您应用隐式转换。 :

consume(hashed_string(hs1));
consume(hashed_string(hs2));
consume(hashed_string("my.png"));

但由于隐式/显式构造函数,它拒绝对 consume(s) 行这样做。

但是请注意,这种保护用户的尝试并非万无一失。如果将字符串声明为数组而不是指针,则可能会不小心重新散列:

const char s[100] = "abc";
consume( s ); // Compiles BUT it's doing implicit, runtime hashing. Doh.

// Decay 's' back to a pointer, and the library's guardrails return
const auto consume_decayed = []( const char* str ) { consume( str ); }
consume_decayed( s ); // Error! Implicit, runtime hashing disallowed!

这种情况不太常见,并且此类数组在传递给其他函数时通常会退化为指针,然后这些函数的行为将如上所示。 可以想象,该库可以使用 if constexpr 等对字符串文字强制执行编译时散列,并禁止它用于非文字数组,如上面的 s。 (这是您回馈图书馆的拉取请求!) [查看评论。]

回答您的最后一个问题:这样做的原因是为了让基于哈希的容器(例如 std::unordered_map)具有更快的性能。它通过计算一次哈希并将其缓存在 hashed_string 中来最大限度地减少您必须执行的哈希数。现在,映射中的键查找只需比较键的预先计算的哈希值和查找字符串。

关于c++ - 在 C++ 中,为什么要重载 `const char array` 上的函数和包装 `const char*` 的私有(private)结构?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/56446068/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com