Sobrecarregando operadores de access a membros ->,. * (C ++)

Eu entendo a maioria das sobrecargas de operadores, com exceção dos operadores de access a membros -> .* , ->* etc.

Em particular, o que é passado para essas funções do operador e o que deve ser retornado?

Como o operador funciona (por exemplo, operator->(...) ) sabe a qual membro está sendo referenciado? Pode saber? Precisa mesmo saber?

Finalmente, há alguma consideração constante que precise ser levada em conta? Por exemplo, ao sobrecarregar algo como operator[] , geralmente você precisará de uma versão const e não const. Os operadores de access a membros exigem versões const e non-const?

    ->

    Este é o único realmente complicado. Deve ser uma function de membro não estática e não requer argumentos. O valor de retorno é usado para executar a consulta de membro.

    Se o valor de retorno for outro object do tipo de class, não um ponteiro, a pesquisa de membro subsequente também será manipulada por uma function operator-> . Isso é chamado de “comportamento de detalhamento”. A linguagem encadeia as chamadas operator-> até que a última retorne um ponteiro.

     struct client { int a; }; struct proxy { client *target; client *operator->() const { return target; } }; struct proxy2 { proxy *target; proxy &operator->() const { return * target; } }; void f() { client x = { 3 }; proxy y = { & x }; proxy2 z = { & y }; std::cout < < xa << y->a < < z->a; // print "333" } 

    ->*

    Este é apenas complicado, pois não há nada de especial nisso. A versão não sobrecarregada requer um object de ponteiro para o tipo de class no lado esquerdo e um object de ponteiro para o tipo de membro à direita. Mas quando você sobrecarrega, você pode pegar qualquer argumento que quiser e retornar o que quiser. Não precisa nem ser um membro não estático.

    Em outras palavras, este é apenas um operador binário normal como + , - e / . Veja também: Operador livre -> * sobrecarrega o mal?

    .* e .

    Estes não podem ser sobrecarregados. Já existe um significado embutido quando o lado esquerdo é do tipo de class. Talvez fizesse algum sentido ser capaz de defini-los para um ponteiro no lado esquerdo, mas o comitê de design de idiomas decidiu que seria mais confuso do que útil.

    Sobrecarga -> , ->* . e .* só pode preencher casos em que uma expressão seria indefinida, nunca pode alterar o significado de uma expressão que seria válida sem sobrecarga.

    Operador -> é especial.

    “Ele tem restrições adicionais e atípicas: ele deve retornar um object (ou referência a um object) que também tenha um operador de referência de ponteiro ou devolver um ponteiro que possa ser usado para selecionar a seta do operador de cancelamento de referência do ponteiro. ” Bruce Eckel: Pensando CPP Vol-1: operador->

    A funcionalidade extra é fornecida por conveniência, para que você não precise chamar

     a->->func(); 

    Você pode simplesmente fazer:

     a->func(); 

    Isso faz com que o operador seja diferente das outras sobrecargas de operador.

    Você não pode sobrecarregar o access de membros . (ou seja, a segunda parte do que -> faz). Você pode, no entanto, sobrecarregar o operador unário de desreferenciação * (ou seja, a primeira parte do que -> faz).

    O operador C ++ -> é basicamente a união de duas etapas e isso fica claro se você acha que x->y é equivalente a (*x).y . C ++ permite que você personalize o que fazer com a parte (*x) quando x for uma instância de sua class.

    A semântica para -> sobrecarga é um pouco estranha porque C ++ permite que você retorne um ponteiro regular (que será usado para encontrar o object apontado) ou retorne uma instância de outra class se esta class também fornecer um operador -> . Quando neste segundo caso, a pesquisa pelo object não referenciado continua dessa nova instância.

    O operador -> não sabe qual membro está sendo apontado, ele apenas fornece um object para executar o access de membro real.

    Além disso, não vejo razão para você não fornecer versões const e non-const.

    Quando você sobrecarrega o operador -> () (nenhum argumento é passado aqui), o que o compilador realmente faz é chamar -> recursivamente até que ele retorne um ponteiro real para um tipo. Em seguida, ele usa o membro / método correto.

    Isso é útil, por exemplo, para criar uma class de ponteiro inteligente que encapsule o ponteiro real. O operador sobrecarregado-> é chamado, faz o que ele faz (por exemplo, bloqueio para segurança de thread), retorna o ponteiro interno e, em seguida, o compilador chama -> para este ponteiro interno.

    Quanto ao constness – foi respondido nos comentários e outras respostas (você pode, e deve, fornecer os dois).