2010-10-14 17 views
26

El operador de asignación se puede sobrecargar el uso de una función miembro, pero no un no miembro friend función:¿Por qué no se puede usar una función no miembro para sobrecargar el operador de asignación?

class Test 
{ 
    int a; 
public: 
    Test(int x) 
     :a(x) 
    {} 
    friend Test& operator=(Test &obj1, Test &obj2); 
}; 

Test& operator=(Test &obj1, Test &obj2)//Not implemented fully. just for test. 
{ 
    return obj1; 
} 

Se hace que este error:

error C2801: 'operator =' must be a non-static member

Por qué no puede una función friend ser utilizado para la sobrecarga de la operador de asignación? El compilador permite sobrecargar otros operadores como += y -= usando friend. ¿Cuál es el problema/limitación inherente al admitir operator=?

Respuesta

25

Dado que el valor predeterminado operator= proporcionado por el compilador (el miembro de copiar uno) siempre tendrá prioridad. Es decir. su amigo operator= nunca sería llamado.

EDIT: Esta respuesta está respondiendo a la parte

Whats the inherent problem/limitation in supporting = operator ?

de la cuestión. Las otras respuestas aquí citan la parte del estándar que dice que no puede hacerlo, pero esto es muy probable por qué esa porción del estándar se escribió de esa manera.

+0

+1: Estoy celoso de la claridad que ha proporcionado detrás del razonamiento real. – Chubsdad

+6

Lo siento, pero eso simplemente es incorrecto y no tiene ningún sentido. ¿Por qué el operador del compilador tiene prioridad? Para aquellos operadores que * pueden * declararse como funciones independientes, declarar la versión de miembro y la versión independiente conduce a * ambigüedad *, no a la función de miembro "tomando precedencia". ¿Cuál es la lógica detrás de la declaración en esta respuesta, entonces? – AnT

+4

@AndreyT y @Billy Oneal: Ambos tienen razón, en diferentes contextos. Si la asignación se realizó dentro de un método de clase, debido a las reglas de búsqueda, la función miembro (en este caso generada por el compilador) tendría prioridad y ocultaría el ámbito de espacio de nombres 'operator ='. Si la asignación ocurre fuera del alcance de la clase, entonces habría ambigüedad y el compilador fallaría. Si bien esto no se puede probar con 'operator =', es bastante simple generar una prueba con 'operator + =' (o cualquier otro operador que se pueda implementar como miembro y como función libre) –

6

Porque hay algunos operadores que DEBEN ser miembros. Estos operadores son:
operator[]
operator=
operator()
operator->

y de conversión de tipo operadores, como operator int.

Aunque uno podría explicar por qué exactamente operator = debe ser miembro, su argumento no puede aplicarse a otros en la lista, lo que me hace creer que la respuesta a "Why" es "Just because".

HTH

+0

Bueno, creo que esa es la pregunta del OP: * ¿por qué * deben ser miembros? – AnT

+0

@AndreyT: editado –

+0

Y operador. no se puede sobrecargar en absoluto, lo que puede ser una pena algunas veces ya que no se puede implementar una referencia inteligente. – CashCow

8

$ 13.5.3 - "Un operador de asignación se llevará a cabo mediante una función miembro no estática con exactamente un parámetro Debido a que un operador operador de asignación de copia = se declara implícitamente por una clase si no declarada por. el usuario (12.8), un operador de asignación de clase base siempre está oculto por el operador de asignación de copia de la clase derivada ".

+0

Eso no explica por qué, por ejemplo, El operador [] no puede sobrecargarse como una función independiente, así que supongo que "Es así como es", es la respuesta más precisa –

+0

+1 - el punto de mi respuesta, pero es mejor citar el estándar respaldando. –

+0

@Armen Tsirunyan: Sí. Traté de hacer referencia a la cita real en el Estándar que habla sobre este aspecto. Las respuestas de Billy y otras dan una buena razón detrás de la misma. – Chubsdad

1

Why friend function can't be used for overloading assignment operator?

Respuesta corta: El hecho de que.

Respuesta algo más larga: Esa es la forma en que se solucionó la sintaxis. Algunos operadores tienen que ser funciones miembro. El operador de asignación es uno de los siguientes,

3

operator= es una función de miembro especial que el compilador proporcionará si no lo declara usted mismo.Debido a este estado especial de operator=, tiene sentido ro requerir que sea una función miembro, por lo que no hay posibilidad de que haya un miembro generado por el compilador operator= y un amigo declarado por el usuario operator= y no haya posibilidad de elegir entre los dos.

31

En primer lugar, debe tenerse en cuenta que esto no tiene nada que ver con el operador que se está implementando como amigo específicamente. Se trata realmente de implementar la función de copia como función miembro o como función no miembro (independiente). Que esa función independiente sea o no amiga es completamente irrelevante: podría ser, tal vez no lo sea, dependiendo de lo que quiera acceder dentro de la clase.

Ahora, la respuesta a esta pregunta se encuentra en D & E book (The Design and Evolution of C++). La razón de esto es que el compilador siempre declara/define un operador de asignación de copia miembro para la clase (si no declara su propio operador de copia-asignación de miembros).

Si el idioma también permitió declarar operador de copia-asignación como un independiente (no miembro) función, que podría terminar con la siguiente

// Class definition 
class SomeClass { 
    // No copy-assignment operator declared here 
    // so the compiler declares its own implicitly 
    ... 
}; 

SomeClass a, b; 

void foo() { 
    a = b; 
    // The code here will use the compiler-declared copy-assignment for `SomeClass` 
    // because it doesn't know anything about any other copy-assignment operators 
} 

// Your standalone assignment operator 
SomeClass& operator =(SomeClass& lhs, const SomeClass& rhs); 

void bar() { 
    a = b; 
    // The code here will use your standalone copy-assigment for `SomeClass` 
    // and not the compiler-declared one 
} 

Como se ve en el ejemplo anterior, la semántica de la copia -la asignación cambiaría en el medio de la unidad de traducción: antes de la declaración de su operador independiente, se utiliza la versión del compilador. Después de la declaración, se usa su versión. El comportamiento del programa cambiará dependiendo de dónde coloque la declaración de su operador de copiado independiente.

Esto se consideró inaceptablemente peligroso (y lo es), por lo que C++ no permite que el operador de copia-asignación se declare como una función independiente.

Es cierto que en el ejemplo particular, que pasa a utilizar una función amigo específicamente, el operador se declara muy temprano, dentro de la definición de clase (ya que así es como se declaran amigos). Entonces, en su caso, el compilador sabrá de inmediato la existencia de su operador. Sin embargo, desde el punto de vista del lenguaje C++, el problema general no está relacionado con las funciones de amigo de ninguna manera. Desde el punto de vista del lenguaje C++, se trata de funciones miembro frente a funciones que no son miembros, y la sobrecarga de la asignación de copias por parte de no miembros está completamente prohibida por los motivos descritos anteriormente.

+0

¿No se puede atrapar haciendo ese uso ambiguo? – Chubsdad

+1

@Chubsdad: Sí, pero ¿cuál sería el punto entonces? Siempre sería ambiguo. No podría usar su operador independiente en absoluto. – AnT

+0

¿Qué es el libro de D & E? Parece interesante ... – Chubsdad

0

La intención de operator= es una operación de asignación al objeto actual. Entonces, el LHS o lvalue es un objeto del mismo tipo.

Considere un caso en el que el LHS es un número entero o de otro tipo. Ese es un caso manejado por operator int() o una función correspondiente operator T(). Por lo tanto, el tipo de LHS ya está definido, pero una función operator= no miembro podría violar esto.

Por lo tanto, se evita.

0

Esta entrada se aplica a C++ 11

Por qué querría alguien que no es miembro operator=? Pues bien, con un miembro de operator= luego el código siguiente es posible:

Test const &ref = (Test() = something); 

que crea una referencia colgando.Un operador que no sea miembro podría solucionar este problema:

Test& operator=(Test &obj1, Test obj2) 

porque ahora el prvalue Test() fallará para unirse a obj1. De hecho, esta firma exigiría que nunca devolviéramos una referencia pendiente (a menos que se nos haya proporcionado una, por supuesto): la función siempre devuelve un valor l "válido" porque exige que se llame con un valor l.

Sin embargo, en C++ 11 ahora hay una manera de especificar que una función miembro sólo puede ser llamado en lvalues, por lo que podría lograr el mismo objetivo al escribir la función de miembro:

Test &operator=(Test obj2) & 
//      ^^^ 

Ahora el el código anterior con referencia colgante no se compilará.


NB. operator= debe tomar el lado derecho por cualquier valor o referencia constante. Tomar por valor es útil al implementar el copy and swap idiom, una técnica para escribir fácilmente operadores de asignación de copia y asignación de movimiento seguros (pero no necesariamente los más rápidos).

+0

Esto fue publicado hace un tiempo, pero sucedió mientras intentaba resolver esto, así que pensé en mencionarlo: sugiriendo que '' operator = '' tomar por valor y luego implementar a través de swap no es un buen consejo. El intercambio predeterminado se implementa a través de la construcción y asignación de movimientos, pero '' operator = '' por valor implementa la asignación de copiar y mover simultáneamente. Esto significa que tendrá que implementar el intercambio (para evitar la recursión), por lo que todavía está implementando la misma cantidad de métodos, pero su asignación de movimiento es mucho más lenta de lo necesario. –

+0

Mejor sugerencia: implemente la asignación de movimiento usted mismo. Luego, obtiene un intercambio eficiente en la mayoría de los casos de forma gratuita (en cualquier escenario, por supuesto, debe implementar la construcción de copia/movimiento). Luego, implemente la asignación de copias usando CAS si prefiere una seguridad de excepción fuerte al rendimiento, de lo contrario impleméntelo desde cero. De cualquier forma, '' operator = '' nunca debe tomarse por valor. –

+0

@NirFriedman actualizado. Depende de los detalles de la clase realmente. En el enfoque de valor por defecto, solo necesita intercambiar en un lugar para poder poner la lógica de intercambio dentro del cuerpo 'operator ='. CAS hace que su código sea muy simple y a salvo de excepciones, pero como usted dice, a menudo no es el más eficiente. –

0

Debido a que existe un operador ya implícita sobrecarga de funciones para '=' en la clase para hacer copia superficial. Así que incluso si sobrecarga utilizando una función de amigo , nunca podrá llamarlo ya que cualquier llamada hecha por nosotros llamaría al método implícito de copia superficial en lugar de a la función amigo sobrecargado.

Cuestiones relacionadas