Reputation:
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.
Upvotes: 0
Views: 106
Reputation: 4299
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.
Upvotes: 0
Reputation: 40901
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
Upvotes: 0