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?
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.
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.
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.
->
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.A->B
cadeias de sintaxe no máximo 1 chamada adicional. O que a sintaxe binária C ++ -> realmente faz não é chamaropeartor->
diretamente o objeto - em vez disso, ele analisa o tipo deA
e verifica se é um ponteiro bruto. Se for, então->
derefs-lo e executarB
em que, caso contrário, ele chama o objeto deoperator->
, derefs o resultado (usando ponteiro bruto nativo ou outrooperator->
e, em seguida, executaB
no resultadox->m
deve ser interpretado como(x.operator->())->m
. Se o LHS for algo com uma sobrecarga adequadaoperator->
novamente, esse processo se repetirá até que haja apenas o(*x).m
efeito usual de 5.2.5 / 2."A linguagem de programação C ++" descreve o fato de que esses operadores são diferentes para que possam ser, mas também diz:
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.
fonte
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).whatever
vez deiter->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.fonte
(*i).m
válido seja suportadoi->m
com a mesma semântica.