как статически преобразовать необработанный массив в структуру, которая состоит только из ссылки на массив
У меня есть критически важный для производительности фрагмент кода, в котором оператор должен возвращать обернутую ссылку на необработанный массив:
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
};
Невозможно заменить B массивом A, поскольку B ссылается на данные из необработанного внешнего кода. Я мог бы обойти эту проблему, заменив функции-члены A на функции, не являющиеся членами float[32], но тогда мне пришлось бы реорганизовать значительную часть существующего кода.
Конечно, заброс в критическую линию сделать невозможно. Но я не хочу мириться ни с избыточностью создания массива A, ни с накладными расходами на создание (и, в конечном итоге, уничтожение) экземпляра A для каждого вызова оператора [].
Итак, как это можно сделать? Пожалуйста, предоставьте функционирующий код, который достигает цели с минимальными (в идеале вообще без) накладными расходами.
2 ответа
имеет только один член ссылочного типа. Это чрезвычайно легко построить/разрушить. Он должен иметь те же накладные расходы, что и возврат указателя.
Просто построить новый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 можно переназначить в C++20, но создание исходного объекта A также всегда требует инициализации. Это можно сделать с фиктивным массивом.
#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];
}
Это позволяет при необходимости динамически изменять в коде то, на что ссылается объект A.