¿Por qué el operador de flecha en C ++ no es solo un alias de *.?


18

En c ++, el operador * puede sobrecargarse, como con un iterador, pero el operador de flecha (->) (. *) No funciona con clases que sobrecarguen el operador *. Me imagino que el preprocesador podría reemplazar fácilmente todas las instancias de -> con (* left) .right, y eso haría que los iteradores sean más fáciles de implementar. ¿Hay alguna razón práctica para que -> sea diferente, o es solo una peculiaridad del lenguaje / diseñadores?

Respuestas:


16

La regla que foo->bares igual (*foo).barsolo es válida para los operadores incorporados.

Unary operator *no siempre tiene la semántica de desreferencia de puntero. Podría hacer una biblioteca en la que signifique transposición de matriz, coincidencias de analizador cero o más, o casi cualquier cosa.

Haría que el lenguaje fuera más molesto si algo que se sobrecargara de operator *repente ganara algo que operator ->usted no solicitó, con una semántica que podría no tener sentido.

operator -> se puede cargar por separado, por lo que si desea uno, puede sobrecargar uno con el mínimo esfuerzo.

También tenga en cuenta que dicha sobrecarga tendría algunas propiedades bastante interesantes, como el encadenamiento automático de operator ->llamadas hasta que uno en la cadena devuelva un puntero sin formato. Esto es bastante útil para punteros inteligentes y otros 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;
}

¿Qué ilustra tu ejemplo? ¿Devuelve un puntero inteligente a una cadena y de alguna manera genera el tamaño? Estoy confundido.
Trevor Hickey

2
Ilustra el último párrafo de mi respuesta, cómo usar las ->cadenas de operador hasta que obtiene un puntero sin formato a algo, desreferenciando y accediendo a un miembro de él. Si el operador -> no encadenara, el ejemplo estaría mal formado ya que shared_ptr no es un puntero sin formato.
Lars Viklund

@LarsViklund: su respuesta tiene un problema: usted dijo "operador-> ... encadena automáticamente las llamadas de operador-> hasta que uno en la cadena devuelve un puntero sin formato". Esto no es correcto: el uso de A->Bcadenas de sintaxis como máximo 1 llamada adicional. Lo que realmente hace la sintaxis binaria C ++ -> no es llamar opeartor->directamente al objeto, sino que analiza el tipo de Ay comprueba si es un puntero sin formato. Si luego lo ->deja y ejecuta Bsobre eso, de lo contrario llama al objeto operator->, deja de lado el resultado (ya sea usando un puntero en bruto nativo u otro operator->y luego se ejecuta Bsobre el resultado
Guss

@Guss: No puedo encontrar ningún capítulo y verso para su reclamo, ni reproducirlo en un compilador. C ++ 11 13.5.6 / 1 indica que si existe una sobrecarga adecuada, x->mse interpretará como (x.operator->())->m. Si el LHS es algo que tiene una sobrecarga adecuada de operator->nuevo, este proceso se repite hasta que se (*x).mproduce el efecto habitual de 5.2.5 / 2.
Lars Viklund

8

"El lenguaje de programación C ++" describe el hecho de que estos operadores son diferentes para que puedan serlo, pero también dice:

Si proporciona más de uno de estos operadores, puede ser conveniente proporcionar la equivalencia, del mismo modo que es aconsejable asegurarse ++xy x+=1tener el mismo efecto que x=x+1para una variable simple xde alguna clase si ++, + =, =, y + se proporcionan.

Por lo tanto, parece que los diseñadores de idiomas proporcionaron puntos de sobrecarga separados porque es posible que desee sobrecargarlos de manera diferente, en lugar de asumir que siempre quiere que sean los mismos.


7

Como regla general, C ++ está diseñado para favorecer la flexibilidad, por lo que las sobrecargas *y ->están separadas. Aunque es bastante inusual hacerlo, si desea lo suficiente, puede escribir esas sobrecargas para hacer cosas completamente diferentes (por ejemplo, podría tener sentido para un lenguaje específico de dominio implementado dentro de C ++).

Dicho esto, iteradores hacen apoyar ya sea su uso. En implementaciones antiguas, puede encontrar una biblioteca que requiera en (*iter).whateverlugar de iter->whatever, pero si es así, es un error en la implementación, no una característica del lenguaje. Dada la cantidad de trabajo involucrado en la implementación de todos los contenedores / algoritmos / iteradores estándar, no es sorprendente que algunas versiones iniciales fueran algo incompletas, pero en realidad nunca tuvieron la intención de ser así.


No me di cuenta de que los contenedores estándar de la biblioteca implementados ->, o que era sobrecargable.
Jakob Weisblat

3
C ++ 03 24.1 / 1 requiere que cualquier iterador donde (*i).msea ​​válido debe ser compatible i->mcon la misma semántica.
Lars Viklund
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.