英文:
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 (&p)[32]; // << 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]; // << this is mandatory
A& operator[](size_t i){ return static_cast<A>(p[i]); } // << 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]; // << this is mandatory
A operator[](unsigned i){ return A{p[i]}; } // << 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 <memory>
struct B {
float(*p)[32]; // << this is mandatory
};
struct A {
inline static float dummy[32]{};
float(&p)[32]=dummy; // << 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& b, size_t index) : p(b.p[index]) {} // ctor from B
A(float(&p)[32]) : p(p) {} // ctor from array ref
void reset_an_A_from_B(const B& 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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论