Esta é uma maneira de usar uma classe proxy para acessar elementos em uma matriz de membro por nome. É muito C ++ e não tem nenhum benefício em relação às funções de acesso de retorno de referência, exceto para preferência sintática. Isso sobrecarrega o ->operador para acessar elementos como membros, portanto, para ser aceitável, é necessário não gostar da sintaxe de acessadores ( d.a() = 5;), bem como tolerar o uso ->com um objeto que não seja um ponteiro. Espero que isso também confunda os leitores não familiarizados com o código, então isso pode ser mais um truque interessante do que algo que você deseja colocar em produção.
A Dataestrutura neste código também inclui sobrecargas para o operador subscrito, para acessar elementos indexados dentro de seu armembro de matriz, bem como begineend funções , para iteração. Além disso, todos eles estão sobrecarregados com versões não constantes e const, que eu senti que precisavam ser incluídas para completar.
Quando Datas ->é usado para acessar um elemento por nome (como este my_data->b = 5;:), um Proxyobjeto é retornado. Então, como esse Proxyrvalue não é um ponteiro, seu próprio ->operador é chamado de cadeia automática, que retorna um ponteiro para si mesmo. Dessa forma, o Proxyobjeto é instanciado e permanece válido durante a avaliação da expressão inicial.
A construção de um Proxyobjeto preenche seus 3 membros de referência a, be de cacordo com um ponteiro passado no construtor, que se supõe apontar para um buffer contendo pelo menos 3 valores cujo tipo é dado como o parâmetro do modelo T. Portanto, em vez de usar referências nomeadas que são membros da Dataclasse, isso economiza memória ao preencher as referências no ponto de acesso (mas, infelizmente, usando ->e não o .operador).
Para testar o quão bem o otimizador do compilador elimina todos os caminhos indiretos introduzidos pelo uso de Proxy, o código a seguir inclui 2 versões de main(). A #if 1versão usa os operadores ->e [], e a #if 0versão executa o conjunto equivalente de procedimentos, mas apenas acessando diretamenteData::ar .
A Nci()função gera valores inteiros de tempo de execução para inicializar elementos da matriz, o que impede o otimizador de apenas inserir valores constantes diretamente em cadastd::cout << chamada.
Para gcc 6.2, usando -O3, ambas as versões de main()geram o mesmo assembly (alterne entre #if 1e #if 0antes do primeiro main()para comparar): 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