如何将原始数组静态转换为仅包含对数组引用的结构体。

huangapple go评论64阅读模式
英文:

how to static cast raw array into struct that only consists of reference to array

问题

我有一段性能关键的代码,其中一个运算符应该返回对原始数组的包装引用:

struct A {
   float (&p)[32]; // 这是 A 的唯一属性;您可以将 p 的类型更改为 float*,但前提是没有其他可能的解决方案。
   // ... 这里是 A 的成员函数
};

struct B {
   float (*p)[32]; // 这是必需的
   A& operator[](size_t i) { return static_cast<A>(p[i]); } // 关键行
};

无法用 A 数组替换 B,因为 B 引用了来自原始外部代码的数据。我可以通过将 A 的成员函数更改为 float[32] 的非成员函数来避开这个问题,但那么我将不得不重构大部分现有代码。

当然,在关键行中的强制转换是行不通的。但是,我既不想接受创建 A 数组的冗余,也不想接受为每个 operator[] 调用构造(最终销毁) A 实例的开销。

那么应该如何实现呢?请提供能够以最小(最好是没有)开销实现目的的可运行代码。

英文:

I have a performance-critical piece of code where an operator shall return a wrapped reference to a raw array:

struct A{
   float (&amp;p)[32]; // &lt;&lt; this is the only attribute of A; you may change the type of p into float* but iff there is no other possible solution.
   // ... here follow member-functions of A
};
struct B{
   float (*p)[32]; // &lt;&lt; this is mandatory
   A&amp; operator[](size_t i){ return static_cast&lt;A&gt;(p[i]); } // &lt;&lt; critical line
};

There is no option to replace B with an array of A, because B references data from raw external code.
I could circumvent the issue by changing the member functions of A to non-member functions on float[32], but then I'd have to refactor a significant portion of existing code.

Of course, the cast in the critical line cannot be made. But, I want to accept neither the redundancy of creating an array of A, nor the overhead of constructing (and eventually destructing) an instance of A for every call of operator[].

So how can it be done. Please provide functioning code that achieves the purpose with minimum (ideally none at all) overhead.

答案1

得分: 1

A 只有一个引用类型的成员。这非常容易构造/析构。它应该具有与返回指针相同的开销。

只需在每次调用时构造一个新的 A 对象:

struct B{
   float (*p)[32]; // << this is mandatory
   A operator[](unsigned i){ return A{p[i]}; } // << critical line
};

与在 float[32] 上使用非成员函数相比,不会有额外开销:https://godbolt.org/z/Md6TP7PPP

英文:

A only has a single member of reference type. This is extremely easy to construct/destruct. It should have the exact same overhead as returning a pointer.

Just construct a new A object with every invocation:

struct B{
   float (*p)[32]; // &lt;&lt; this is mandatory
   A operator[](unsigned i){ return A{p[i]}; } // &lt;&lt; critical line
};

And there will be no overhead versus using non-member functions on float[32]: https://godbolt.org/z/Md6TP7PPP

答案2

得分: 0

A 类的包装器可以在 C++20 中重新定位,但创建初始的 A 对象始终需要初始化。这可以通过一个虚拟数组来实现。

#include <memory>

struct B {
    float(*p)[32]; // << 这是必需的
};

struct A {
    inline static float dummy[32]{};
    float(&p)[32] = dummy; // << 这是 A 的唯一属性;你可以将 p 的类型更改为 float*,但前提是没有其他可能的解决方案。
    // ... 这里跟随 A 的成员函数
    A() : p(p) {}  // 从 B 构造函数
    A(const B& b, size_t index) : p(b.p[index]) {}  // 从 B 构造函数
    A(float(&p)[32]) : p(p) {}  // 从数组引用构造函数
    void reset_an_A_from_B(const B& b, size_t index) {
        std::destroy_at(this);  // 在这个示例中不需要,因为 A 是可以平凡销毁的
        std::construct_at(this, A{ b.p[index] });
    };
};

int main()
{
    float external_arrays[2][32]{};
    B b1{external_arrays};     // 包含两个 32 个浮点数的数组

    A a(b1,0);  // 从 b 构造一个 A,使用第一个(也是唯一的)数组
    a.reset_an_A_from_B(b1, 1);    // 重置 a,使其引用 b 中的第二个数组

    // 或者,构造一个由虚拟静态数组初始化的 A
    A a1;
    a1.reset_an_A_from_B(b1, 0);    // 然后重置 a,使其引用 B 对象中的第一个数组

    // 要使用 A 中的引用
    float x = a1.p[31];
}

这允许在代码中根据需要动态更改 A 对象引用的内容。

英文:

The wrapper for A can be retargeted in c++20 but creating an initial A object also always requires initialization. This can be done to a dummy array.

#include &lt;memory&gt;

struct B {
    float(*p)[32]; // &lt;&lt; this is mandatory
};

struct A {
    inline static float dummy[32]{};
    float(&amp;p)[32]=dummy; // &lt;&lt; this is the only attribute of A; you may change the type of p into float* but iff there is no other possible solution.
    // ... here follow member-functions of A
    A() : p(p) {}  // ctor from B
    A(const B&amp; b, size_t index) : p(b.p[index]) {}  // ctor from B
    A(float(&amp;p)[32]) : p(p) {}  // ctor from array ref
    void reset_an_A_from_B(const B&amp; b, size_t index) {
        std::destroy_at(this);  // this is not needed in this example since A is trivially destructable
        std::construct_at(this, A{ b.p[index] });
    };
};


int main()
{
    float external_arrays[2][32]{};
    B b1{external_arrays};     // contains two arrays of 32 floats

    A a(b1,0);  // construct a from b, first (and only) array
    a.reset_an_A_from_B(b1, 1);    // reset a to refer to second array in b

    // alternately, construct an A initialized by the dummy static array
    A a1;
    a1.reset_an_A_from_B(b1, 0);    // then reset a to refer to first array in a B object

    // To use the reference in A
    float x = a1.p[31];
}

This allows one to change what an A object refers to dynamically in the code as needed.

huangapple
  • 本文由 发表于 2023年6月11日 20:50:48
  • 转载请务必保留本文链接:https://go.coder-hub.com/76450544.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定