我正在尝试根据 CRTP 样式框架中的基类为一堆派生类编写一次模板专门化。但是,我无法编译它。
我有一个基类,想要生成多种派生类。基类命名了一些将返回派生类类型的对象的方法。因此,我一直在使用 CRTP 模式来耦合两者(遗憾的是,C++23 的“推导这个”不是一个选项,因为在撰写本文时大多数编译器尚不支持它)。
我希望能够对这些对象进行哈希处理(这样我就可以将它们放入某些 STL 容器中),因此我尝试编写
std::hash
的专门化,但我遇到了困难。我可以根据派生类编写专门化的类,但无法弄清楚如何根据基类编写通用类。
#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>
。
此外,您应该将
dynamic_cast<Self const*>(this)
更改为 static_cast<Self const*>
,因为您在编译时就知道转换是有效的。