派生 类 中的 sizeof(*this) 和 decltype(*this)
sizeof(*this) and decltype(*this) in derived classes
假设有class个:
struct A {
int a;
virtual size_t GetMemoryUsage() const {
return sizeof(*this);
}
};
struct B : public A {
int b;
};
而且可能还有更深层次的传承。
我想要的是有一个方法可以 return 对象在内存中占用的字节数,在本例中为 GetMemoryUsage()
。通常使用sizeof(*this)
即可实现。问题是(至少 AFAIU)我必须重写每个派生 class 中的方法并实际复制粘贴其主体。我不喜欢重复的代码:)
我说的对吗?我怎样才能使 sizeof(*this)
和 decltype(*this)
return 在 subclasses 中成为我想要的,只从基础 class 的方法中调用它们?有没有更优雅的解决方案?
您不必为每个派生的 class 手动实现 GetMemoryUsage
,只需将其保留为纯虚拟即可。例如:
struct A
{
virtual ~A() = default;
virtual size_t GetMemoryUsage() const noexcept = 0;
};
struct B : A
{
int b;
};
但是,在创建对象时,必须实现该功能。您可以使用 "decorates" class 的工厂函数以及纯虚拟的通用实现来做到这一点:
// Can alternatively be defined inside function template create.
template<class T>
struct GetMemoryUsageImpl : T
{
using T::T;
size_t GetMemoryUsage() const noexcept final {
return sizeof(T);
}
};
template<class T, class... Args>
std::unique_ptr<T> create(Args&&... args) {
return std::unique_ptr<T>(new GetMemoryUsageImpl<T>(std::forward<Args>(args)...));
}
用法:
void f(A& a) {
auto object_size = a.GetMemoryUsage();
}
int main() {
auto b = create<B>();
f(*b);
}
You can also implement a hierarchy of interfaces incrementally easily using this idiom.
这是@Maxim 解决方案的疯狂通用版本。
template<class B0, template<class...>class... Z>
struct TemplateFold {
using type=B0;
};
template<class B0, template<class...>class... Z>
using TemplateFold_t = typename TemplateFold<B0, Z...>::type;
template<class B0, template<class...>class Z0, template<class...>class... Z>
struct TemplateFold<B0, Z0, Z...>
{
using type=Z0< TemplateFold_t<B0, Z...> >;
};
struct ExposeTrivial {
protected:
~ExposeTrivial() {}
};
template<class D, class B0=ExposeTrivial, class...Bases>
struct Expose:B0, Bases... {
// is a template because D isn't a real type when this class is instantiated:
template<class T>
using MakeConcreteType = TemplateFold_t< T, std::conditional_t< std::is_same<B0,ExposeTrivial>{}, T, B0 >::template Implement, Bases::template Implement... >;
template<class...Args>
static std::unique_ptr<D> create( Args&&... args ) {
using ConcreteType = MakeConcreteType<D>;
return std::unique_ptr<D>( new ConcreteType( std::forward<Args>(args)... ) );
}
protected:
~Expose() {}
};
// expose one thing:
struct ExposeMemoryUsage:Expose<ExposeMemoryUsage> {
virtual std::size_t GetMemoryUsage() const noexcept = 0;
template<class B>
struct Implement:B {
using B::B;
std::size_t GetMemoryUsage() const noexcept override final {
return sizeof(*this);
}
};
protected:
~ExposeMemoryUsage() {}
};
// expose a different thing:
struct ExposeAlignment:Expose<ExposeAlignment>{
virtual std::size_t GetAlignment() const noexcept = 0;
template<class B>
struct Implement:B {
using B::B;
std::size_t GetAlignment() const noexcept final override {
return alignof(decltype(*this));
}
};
};
// Expose two things:
struct Bob : Expose<Bob, ExposeMemoryUsage, ExposeAlignment> {
int x;
Bob( int v ): x(v) {}
virtual ~Bob() {}
};
int main() {
std::unique_ptr<Bob> ptr = Bob::create(7);
std::cout << ptr->x << " size:" << ptr->GetMemoryUsage() << " align:" << ptr->GetAlignment() << "\n";
// Bob b; // does not compile
}
只需在 Exposer
中添加更多 "knows the derived type" 个静态助手以增加功能。
使用方法:
创建一个 Expose 类型。它应该有一个纯虚拟成员和一个实现该纯虚拟成员的模板实现 class(给定一个从 Expose 类型派生的 class)。
应该继承Expose<OwnType>
(CRTP)给你写静态::create
方法
如果您想继承其他 Expose
类型(即组合两个需要知道具体类型的独立 Expose 接口),而不是继承 Expose< YourType, OtherExposeType, AnotherExposeType >
。不要独立继承 OtherExposeType
或 AnotherExposeType
.
如果您这样做,您的 Implement
模板将不会被选取。
我可以对此进行改进,以便我们在您和您的基地中检测到 Implement
模板,但这比我现在准备的要多得多。
假设有class个:
struct A {
int a;
virtual size_t GetMemoryUsage() const {
return sizeof(*this);
}
};
struct B : public A {
int b;
};
而且可能还有更深层次的传承。
我想要的是有一个方法可以 return 对象在内存中占用的字节数,在本例中为 GetMemoryUsage()
。通常使用sizeof(*this)
即可实现。问题是(至少 AFAIU)我必须重写每个派生 class 中的方法并实际复制粘贴其主体。我不喜欢重复的代码:)
我说的对吗?我怎样才能使 sizeof(*this)
和 decltype(*this)
return 在 subclasses 中成为我想要的,只从基础 class 的方法中调用它们?有没有更优雅的解决方案?
您不必为每个派生的 class 手动实现 GetMemoryUsage
,只需将其保留为纯虚拟即可。例如:
struct A
{
virtual ~A() = default;
virtual size_t GetMemoryUsage() const noexcept = 0;
};
struct B : A
{
int b;
};
但是,在创建对象时,必须实现该功能。您可以使用 "decorates" class 的工厂函数以及纯虚拟的通用实现来做到这一点:
// Can alternatively be defined inside function template create.
template<class T>
struct GetMemoryUsageImpl : T
{
using T::T;
size_t GetMemoryUsage() const noexcept final {
return sizeof(T);
}
};
template<class T, class... Args>
std::unique_ptr<T> create(Args&&... args) {
return std::unique_ptr<T>(new GetMemoryUsageImpl<T>(std::forward<Args>(args)...));
}
用法:
void f(A& a) {
auto object_size = a.GetMemoryUsage();
}
int main() {
auto b = create<B>();
f(*b);
}
You can also implement a hierarchy of interfaces incrementally easily using this idiom.
这是@Maxim 解决方案的疯狂通用版本。
template<class B0, template<class...>class... Z>
struct TemplateFold {
using type=B0;
};
template<class B0, template<class...>class... Z>
using TemplateFold_t = typename TemplateFold<B0, Z...>::type;
template<class B0, template<class...>class Z0, template<class...>class... Z>
struct TemplateFold<B0, Z0, Z...>
{
using type=Z0< TemplateFold_t<B0, Z...> >;
};
struct ExposeTrivial {
protected:
~ExposeTrivial() {}
};
template<class D, class B0=ExposeTrivial, class...Bases>
struct Expose:B0, Bases... {
// is a template because D isn't a real type when this class is instantiated:
template<class T>
using MakeConcreteType = TemplateFold_t< T, std::conditional_t< std::is_same<B0,ExposeTrivial>{}, T, B0 >::template Implement, Bases::template Implement... >;
template<class...Args>
static std::unique_ptr<D> create( Args&&... args ) {
using ConcreteType = MakeConcreteType<D>;
return std::unique_ptr<D>( new ConcreteType( std::forward<Args>(args)... ) );
}
protected:
~Expose() {}
};
// expose one thing:
struct ExposeMemoryUsage:Expose<ExposeMemoryUsage> {
virtual std::size_t GetMemoryUsage() const noexcept = 0;
template<class B>
struct Implement:B {
using B::B;
std::size_t GetMemoryUsage() const noexcept override final {
return sizeof(*this);
}
};
protected:
~ExposeMemoryUsage() {}
};
// expose a different thing:
struct ExposeAlignment:Expose<ExposeAlignment>{
virtual std::size_t GetAlignment() const noexcept = 0;
template<class B>
struct Implement:B {
using B::B;
std::size_t GetAlignment() const noexcept final override {
return alignof(decltype(*this));
}
};
};
// Expose two things:
struct Bob : Expose<Bob, ExposeMemoryUsage, ExposeAlignment> {
int x;
Bob( int v ): x(v) {}
virtual ~Bob() {}
};
int main() {
std::unique_ptr<Bob> ptr = Bob::create(7);
std::cout << ptr->x << " size:" << ptr->GetMemoryUsage() << " align:" << ptr->GetAlignment() << "\n";
// Bob b; // does not compile
}
只需在 Exposer
中添加更多 "knows the derived type" 个静态助手以增加功能。
使用方法:
创建一个 Expose 类型。它应该有一个纯虚拟成员和一个实现该纯虚拟成员的模板实现 class(给定一个从 Expose 类型派生的 class)。
应该继承Expose<OwnType>
(CRTP)给你写静态::create
方法
如果您想继承其他 Expose
类型(即组合两个需要知道具体类型的独立 Expose 接口),而不是继承 Expose< YourType, OtherExposeType, AnotherExposeType >
。不要独立继承 OtherExposeType
或 AnotherExposeType
.
如果您这样做,您的 Implement
模板将不会被选取。
我可以对此进行改进,以便我们在您和您的基地中检测到 Implement
模板,但这比我现在准备的要多得多。