提问人:oliversm 提问时间:9/11/2023 更新时间:9/11/2023 访问量:68
CRTP 根据 std::hash 的基类为派生类提供专业化
CRTP provide specialisation for derived classes in terms of base class for std::hash
问:
顶级域名
我正在尝试根据 CRTP 样式框架中的基类为一堆派生类编写一次模板专用化。但是,我无法让它编译。
我想做什么
我有一个基类,想要生成几种派生类。基类命名了一些方法,这些方法将返回派生类类型的对象。正因为如此,我一直在使用 CRTP 模式将两者耦合(可悲的是,C++23 的“推导”不是一个选项,因为在撰写本文时,大多数编译器还不支持它)。
我希望能够对这些对象进行哈希处理(这样我就可以将它们放在某些 STL 容器中),所以我正在尝试编写一个 的专业化,但我没有卡住。我可以根据派生类编写专业化,但无法弄清楚如何根据基类编写通用专业化。std::hash
代码
基类和派生类以及 CRTP
#include <vector>
template<typename Self>
class Base {
protected:
using my_type = int;
std::vector<my_type> bar; // <- I will be doing something special to hash this...
public:
virtual void baz(void) const = 0;
Self foo(void) const {
Self result = *dynamic_cast<const Self *>(this);
// do something with the private bar variable.
return result;
};
};
class Derived_A final : public Base<Derived_A> {
public:
void baz(void) const override {}
};
class Derived_B : public Base<Derived_B> {
public:
void baz(void) const override {}
};
我想如何使用这些
int main() {
Derived_A d_a;
Derived_B d_b;
auto d_a_ = d_a.foo();
d_a.baz();
std::hash<Derived_B>{}(d_b);
std::hash<Derived_A>{}(d_a); // <-- This doesn't compile
}
模板专业化
// Works.
template<>
struct std::hash<Derived_B> {
std::size_t operator()(const auto &shape) const noexcept { return 0; }
};
// Not working.
template<typename Self>
struct std::hash<Base<Self>> {
std::size_t operator()(const Base<Self> &shape) const noexcept { return 0; }
};
编译器说什么
error: call to deleted constructor of '__enum_hash<Derived_A>'
std::hash<Derived_A>{}(d_a);
^
/usr/local/opt/llvm/bin/../include/c++/v1/__functional/hash.h:643:5: note: '__enum_hash' has been explicitly marked deleted here
__enum_hash() = delete;
答:
你要找的是这个:
template<typename T> requires std::is_base_of_v<Base<T>, T>
struct std::hash<T> {
std::size_t operator()(T const& shape) const noexcept { return 0; }
};
你匹配任何扩展的东西。T
Base<T>
之所以这样做而不是前一个,是因为模板和继承之间的关系 - 或缺乏关系。
实例化类型与任何其他实例化类型没有关系。 与 相比,是一种完全不同且不相关的类型。std::vector<int>
std::vector<float>
对于彼此之间有关系的类型也是如此。例如,也是完全不相关的类型。当容器实例化时,它将精确地实例化该类型,并且只实例化该类型。由于您之前的代码只提供了 的哈希值,因此该类型必须回退到导致编译错误的非专用类型。std::hash<A>
std::hash<Base<A>>
std::hash<A>
std::hash<Base<A>>
std::hash<A>
奖励,您应该更改为 ,因为您在编译时知道转换是有效的。dynamic_cast<Self const*>(this)
static_cast<Self const*>
如果您想更加确定可以在 base 中添加这样的私有方法:
auto self() const& -> Self const& {
static_assert(std::is_base_of_v<Base<Self>, Self>);
return *static_cast<Self const&>(this);
}
auto self() & -> Self& {
static_assert(std::is_base_of_v<Base<Self>, Self>);
return *static_cast<Self&>(this);
}
评论
requires ...
上一个:使用变体向量调用正确的函数专用化
下一个:模板函数覆盖
评论
class D : public B {}
std::hash<D>
std::hash<B>
std::hash<D>{}
std::hash<B>
std::hash<Derived_A>
std::hash<Base<Derived_A>>