Por que o operador de seta no C ++ não é apenas um alias de *.?


18

Em c ++, o operador * pode ser sobrecarregado, como em um iterador, mas o operador de seta (->) (. *) Não funciona com classes que sobrecarregam o operador *. Eu imagino que o pré-processador possa substituir facilmente todas as instâncias de -> por (* left) .right, e isso tornaria os iteradores mais agradáveis ​​de implementar. existe uma razão prática para -> ser diferente ou isso é apenas uma peculiaridade da linguagem / designers?

Respostas:


16

A regra foo->barigual a (*foo).barapenas é válida para os operadores internos.

O Unary operator *nem sempre tem a semântica de desreferência de ponteiro. Eu poderia criar uma biblioteca na qual isso significa transposição de matriz, zero ou mais correspondências do analisador ou praticamente qualquer coisa.

Isso tornaria a linguagem mais incômoda se algo que sobrecarregasse o unário operator *ganhasse de repente um que operator ->você não pedisse, com semânticas que talvez não fizessem sentido.

operator -> é sobrecarregável separadamente; portanto, se você quiser um, pode sobrecarregar um com o mínimo esforço.

Observe também que essa sobrecarga teria algumas propriedades bastante interessantes, como encadear operator ->chamadas automaticamente até que uma na cadeia retorne um ponteiro bruto. Isso é bastante útil para ponteiros inteligentes e outros tipos de proxy.

#include <boost/make_shared.hpp>
#include <boost/shared_ptr.hpp>
#include <string>
#include <iostream>
#include <ostream>

struct Foo
{
    boost::shared_ptr<std::string> operator -> () const
    {
        return boost::make_shared<std::string>("trololo");
    }
};

int main()
{
    Foo foo;
    std::cerr << foo->size() << std::endl;
}

O que seu exemplo ilustra? Você está retornando um ponteiro inteligente para uma string e, de alguma forma, produzindo o tamanho? Estou confuso.
Trevor Hickey

2
Ele ilustra o último parágrafo da minha resposta, como usar as ->cadeias de operadores até obter um ponteiro bruto para alguma coisa, desreferenciando e acessando um membro dela. Se o operador -> não encadear, o exemplo seria mal formado, pois shared_ptr não é um ponteiro bruto.
Lars Viklund

@LarsViklund: sua resposta tem um problema: você disse que "operator-> ... encadeia automaticamente operator-> calls até que um na cadeia retorne um ponteiro bruto". Isso não está correto - usando A->Bcadeias de sintaxe no máximo 1 chamada adicional. O que a sintaxe binária C ++ -> realmente faz não é chamar opeartor->diretamente o objeto - em vez disso, ele analisa o tipo de Ae verifica se é um ponteiro bruto. Se for, então ->derefs-lo e executar Bem que, caso contrário, ele chama o objeto de operator->, derefs o resultado (usando ponteiro bruto nativo ou outro operator->e, em seguida, executa Bno resultado
Guss

@ Guss: Não consigo encontrar nenhum capítulo e verso para sua reivindicação, nem reproduzi-lo em um compilador. C ++ 11 13.5.6 / 1 indica que, se existir uma sobrecarga adequada, x->mdeve ser interpretado como (x.operator->())->m. Se o LHS for algo com uma sobrecarga adequada operator->novamente, esse processo se repetirá até que haja apenas o (*x).mefeito usual de 5.2.5 / 2.
Lars Viklund

8

"A linguagem de programação C ++" descreve o fato de que esses operadores são diferentes para que possam ser, mas também diz:

Se você fornecer mais de um desses operadores, pode ser aconselhável fornecer a equivalência, assim como é aconselhável garantir isso ++xe x+=1ter o mesmo efeito que x=x+1para uma variável simples xde alguma classe se ++, + =, = e + são fornecidos.

Parece que os designers de idiomas forneceram pontos de sobrecarga separados porque você pode sobrecarregá-los de maneira diferente, em vez de assumir que sempre deseja que eles sejam iguais.


7

Como regra geral, o C ++ é projetado para favorecer a flexibilidade; portanto, as sobrecargas *e ->as separam. Embora seja bastante incomum fazer isso, se você quiser o suficiente, pode escrever essas sobrecargas para fazer coisas totalmente diferentes (por exemplo, pode fazer sentido para uma linguagem específica de domínio implementada dentro do C ++).

Dito isto, iterators fazer suportar tanto uso. Em implementações antigas, você pode encontrar uma biblioteca que requer, em (*iter).whatevervez de iter->whatever, mas se for o caso, isso é um bug na implementação, não uma característica da linguagem. Dada a quantidade de trabalho envolvido na implementação de todos os contêineres / algoritmos / iteradores padrão, não é de surpreender que alguns lançamentos anteriores tenham sido um pouco incompletos, mas eles nunca foram realmente destinados a ser assim.


Não percebi que os contêineres de biblioteca padrão foram implementados -> ou que eram sobrecarregáveis.
Jakob Weisblat 19/07/12

3
O C ++ 03 24.1 / 1 requer que qualquer iterador (*i).mválido seja suportado i->mcom a mesma semântica.
Lars Viklund
Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.