Вот способ использовать прокси-класс для доступа к элементам в массиве элементов по имени. Он очень похож на C ++ и не имеет преимуществ перед функциями доступа, возвращающими ref, за исключением синтаксических предпочтений. Это перегружает ->
оператора для доступа к элементам как членам, поэтому, чтобы быть приемлемым, нужно не только не любить синтаксис accessors ( d.a() = 5;
), но и допускать использование ->
с объектом, не являющимся указателем. Я полагаю, что это также может сбить с толку читателей, не знакомых с кодом, так что это может быть скорее хитрый трюк, чем то, что вы хотите внедрить в производство.
Структура Data
в этом коде также включает перегрузки для оператора нижнего индекса для доступа к индексированным элементам внутри его ar
члена массива, а также функции begin
и end
для итерации. Кроме того, все они перегружены неконстантными и константными версиями, которые, как я чувствовал, необходимо включить для полноты.
Когда Data
«S ->
используется для доступа к элементу по имени (например: my_data->b = 5;
), А Proxy
объект возвращается. Затем, поскольку это Proxy
rvalue не является указателем, ->
автоматически вызывается его собственный оператор, который возвращает указатель на себя. Таким образом Proxy
создается экземпляр объекта, который остается действительным во время оценки исходного выражения.
Создание Proxy
объекта заполняет его 3 ссылочных члена a
, b
и в c
соответствии с указателем, переданным в конструктор, предполагается, что он указывает на буфер, содержащий по крайней мере 3 значения, тип которых указан как параметр шаблона T
. Таким образом, вместо использования именованных ссылок, которые являются членами Data
класса, это экономит память, заполняя ссылки в точке доступа (но, к сожалению, используя, ->
а не .
оператор).
Чтобы проверить, насколько хорошо оптимизатор компилятора устраняет все косвенные обращения, возникающие при использовании Proxy
, приведенный ниже код включает 2 версии main()
. #if 1
Версия использует ->
и []
оператор, а также #if 0
версия выполняет эквивалентный набор процедур, но только путем непосредственного доступа Data::ar
.
Nci()
Функция генерирует во время выполнения целочисленных значений для инициализации элементов массива, который предотвращает оптимизатор от только подключить постоянные значения непосредственно в каждый std::cout
<<
вызов.
Для gcc 6.2 при использовании -O3 обе версии main()
генерируют одну и ту же сборку (переключение между #if 1
и #if 0
перед первой main()
для сравнения): https://godbolt.org/g/QqRWZb
#include <iostream>
#include <ctime>
template <typename T>
class Proxy {
public:
T &a, &b, &c;
Proxy(T* par) : a(par[0]), b(par[1]), c(par[2]) {}
Proxy* operator -> () { return this; }
};
struct Data {
int ar[3];
template <typename I> int& operator [] (I idx) { return ar[idx]; }
template <typename I> const int& operator [] (I idx) const { return ar[idx]; }
Proxy<int> operator -> () { return Proxy<int>(ar); }
Proxy<const int> operator -> () const { return Proxy<const int>(ar); }
int* begin() { return ar; }
const int* begin() const { return ar; }
int* end() { return ar + sizeof(ar)/sizeof(int); }
const int* end() const { return ar + sizeof(ar)/sizeof(int); }
};
// Nci returns an unpredictible int
inline int Nci() {
static auto t = std::time(nullptr) / 100 * 100;
return static_cast<int>(t++ % 1000);
}
#if 1
int main() {
Data d = {Nci(), Nci(), Nci()};
for(auto v : d) { std::cout << v << ' '; }
std::cout << "\n";
std::cout << d->b << "\n";
d->b = -5;
std::cout << d[1] << "\n";
std::cout << "\n";
const Data cd = {Nci(), Nci(), Nci()};
for(auto v : cd) { std::cout << v << ' '; }
std::cout << "\n";
std::cout << cd->c << "\n";
//cd->c = -5; // error: assignment of read-only location
std::cout << cd[2] << "\n";
}
#else
int main() {
Data d = {Nci(), Nci(), Nci()};
for(auto v : d.ar) { std::cout << v << ' '; }
std::cout << "\n";
std::cout << d.ar[1] << "\n";
d->b = -5;
std::cout << d.ar[1] << "\n";
std::cout << "\n";
const Data cd = {Nci(), Nci(), Nci()};
for(auto v : cd.ar) { std::cout << v << ' '; }
std::cout << "\n";
std::cout << cd.ar[2] << "\n";
//cd.ar[2] = -5;
std::cout << cd.ar[2] << "\n";
}
#endif