gpt4 book ai didi

CRTP provide specialisation for derived classes in terms of base class for std::hash(CRTP根据std::hash的基类为派生类提供专门化)

转载 作者:bug小助手 更新时间:2023-10-25 11:33:37 28 4
gpt4 key购买 nike



TLDR


I am trying to write a template specialisation for a bunch of derived classes only once in terms of the base class in a CRTP style framework. However, I can't get it to compile.

我正在尝试编写一组派生类的模板专门化,只针对CRTP样式框架中的基类编写一次。但是,我无法对其进行编译。


What I am trying to do


I have a base class and want to produce several flavours of derived classes. The base class names a few methods which would return objects of the type of the derived class. Because of this, I have been using the CRTP pattern to couple the two (C++23's "deducing this" is not an option sadly because at the time of writing most compilers don't support it yet).

我有一个基类,想要生成几种风格的派生类。基类命名了几个将返回派生类类型的对象的方法。正因为如此,我一直在使用CRTP模式将两者结合在一起(遗憾的是,C++23的S不能“推断这一点”,因为在撰写本文时,大多数编译器还不支持它)。


I want to be able to hash these objects (so I can put them in certain STL containers), so I am trying to write a specialisation of std::hash, but am coming unstuck. I can write a specialisation in terms of a derived class, but can't figure out how to write a general one in terms of the base class.

我希望能够散列这些对象(这样我就可以将它们放在特定的STL容器中),所以我正在尝试编写std::hash的专门化,但我失败了。我可以编写派生类方面的专门化,但不知道如何编写基类方面的通用专门化。


The code


The base and derived classes and the 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 {}
};

How I want to use these


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
}

The template specialisations


// 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; }
};

What the compiler says


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;

更多回答

Even if you have class D : public B {}; still std::hash<D> and std::hash<B> are two distinct, unrelated classes: writing std::hash<D>{} doesn't magically instantiate std::hash<B>. Similarly, there's no relationship between std::hash<Derived_A> and std::hash<Base<Derived_A>>

即使您有类D:PUBLIC B{};std::hash和std::hash仍然是两个不同的、不相关的类:编写std::hash{}不会神奇地实例化std::hash。同样,std::hash和std::hash之间也没有关系

优秀答案推荐

What you're looking for is this:

你要找的是这个:


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; }
};

You match for any T what extends Base<T>.

可以匹配任何扩展Base 的T。


The reason why this works and not the previous one is because of the relationship between templates and inheritance - or lack thereof.

这种方法之所以有效,而不是前一种方法,是因为模板和继承之间的关系--或者说缺乏这种关系。


An instantiated type has no relationship with any other instantiated types. std::vector<int> is a completely different and unrelated type compared to std::vector<float>.

实例化类型与任何其他实例化类型没有关系。与std::VECTOR 相比,STD::VECTOR 是一个完全不同且无关的类型。


The same is true for types that have relationship between each other. So for example, std::hash<A> and std::hash<Base<A>> are also completely unrelated types. When a container instantiate std::hash<A>, it will instantiate that type and only that type precisely. As your previous code did only provide hashes for std::hash<Base<A>>, the type std::hash<A> had to fallback to the non specialized one which caused the compilation error.

对于彼此有关系的类型也是如此。因此,例如,std::hash和std::hash 也是完全无关的类型。当容器实例化std::hash 时,它将准确地实例化该类型且仅实例化该类型。由于您之前的代码确实只为std::hash 提供了散列,因此std::hash 类型必须后退到导致编译错误的非专门化类型。




Bonus, you should change the dynamic_cast<Self const*>(this) to static_cast<Self const*> since you know at compile time that the conversion is valid.

另外,您应该将DYNAMIC_CAST (This)更改为STATIC_CAST ,因为您在编译时知道转换是有效的。


If you want to be extra sure you can add such private method in base:

如果您想要特别确定,可以在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);
}

更多回答

Thanks. Are you able to provide a bit of explanation / know-how about why the template with the requires ... gets picked up, and what was going wrong with my attempt, so I can better understand what's going on/wrong under the hood.

谢谢。你能提供一点关于为什么模板需要的解释/诀窍吗?被选中,以及我的尝试出了什么问题,这样我就可以更好地了解引擎盖下面发生了什么/错误。

@oliversm let me edit

@oleverm让我编辑

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