直接重置结构体的内容

问题描述

我实现了一个函数来重置指针指向的结构的内容

template <typename Struct>
void initialize(Struct* s)
{
    *s = Struct{};
}

Struct 变大(超过 10K)时,我会遇到性能问题,因为 Struct 在堆栈中创建,然后分配给 *s。我想知道我是否可以改进它:

  1. 是否可以在没有临时 *s 对象的情况下直接初始化 Struct{}
  2. 如果 Struct 的大小很大,我应该评估它的大小并在堆中构建它吗?

提前致谢

解决方法

首先,您可能应该使用参考;不是指针。这样做的目的是避免空间接错误。

如果类是平凡的并且值初始化为零(这是大多数平凡类型的通常情况),那么优化器应该将您的函数编译为对 memset 的调用,而不是任何需要初始化临时对象。所以在这种情况下应该没有理由担心。

您可以显式调用 memset,尽管这在技术上不能移植到外来系统,以防类包含某些类型(例如,空指针不一定具有零的表示)。

是否可以在没有临时 Struct{} 对象的情况下直接初始化 *s?。

是的,如果您愿意更改函数的要求。目前它适用于默认可构造和可移动分配的类。

如果直接修改指向的对象,则可以避免创建临时对象。在以下示例中,没有创建 Struct 类型的临时对象:

constexpr void
initialize(Struct& s)
{
    s.member1 = T1{};
    s.member2 = T2{};

为了使其通用,可以在成员函数中执行操作。因此,您可以指定一个要求,即所指向的类具有一个具有特定名称且没有参数的成员函数:

s.clear();

对于它们适用的类型,您可以将这两种方法结合起来:

template<class Struct>
constexpr void
initialize(Struct& s)
{
    if constexpr (std::is_trivially_copyable_v<Struct>) {
        // alternative 1,if you trust your optimiser
        s = Struct{};
        // alternative 2,if you doubt the quality of the optimiser
        //    technically may have different meaning on exotic systems
        std::memset(&s,sizeof s);
    } else {
        s.clear();
    }
}

如果您需要它来处理一些既不符合要求又不符合要求的类,那么您需要专门化模板。

如果 Struct 很大 [10K],我应该评估它的大小并在堆中构建它吗?

您通常应该避免完全使用那么大的公共类。如果您需要如此大的存储空间,您可以将其包装在动态分配的类型中。像这样:

class Struct
{
private:
    struct VeryLarge{/.../};
    std::unique_ptr<VeryLarge> storage;
public:
    // public interface