c++ - relacionales - ¿Por qué no se puede usar una función no miembro para sobrecargar el operador de asignación?
sobrecarga de operadores relacionales en c++ (9)
¿Por qué la función de amigo no se puede usar para sobrecargar al operador de asignación?
Respuesta corta: solo porque .
Respuesta algo más larga: esa es la forma en que se solucionó la sintaxis. Algunos operadores deben ser funciones miembro . El operador de asignación es uno de los
El operador de asignación puede estar sobrecargado usando una función de miembro pero no una función de friend
no miembro:
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;
}
Causa este error:
error C2801: ''operator ='' debe ser un miembro no estático
¿Por qué no se puede utilizar una función friend
para sobrecargar al operador de asignación? El compilador permite sobrecargar a otros operadores como +=
y -=
usando friend
. ¿Cuál es el problema / limitación inherente en el operator=
soporte operator=
?
$ 13.5.3 - "Un operador de asignación debe ser implementado por una función de miembro no estático con exactamente un parámetro. Porque un operador de asignación de copia = se declara implícitamente para una clase si el usuario no lo declara (12.8), una asignación de clase base el operador siempre está oculto por el operador de asignación de copias de la clase derivada ".
Debido a que el operator=
predeterminado operator=
proporcionado por el compilador (el miembro de copiar uno) siempre tendrá prioridad. Es decir, su operator=
amigo operator=
nunca sería llamado.
EDITAR: Esta respuesta está respondiendo al
¿Cuál es el problema / limitación inherente en support = operator?
parte de la pregunta. Las otras respuestas aquí citan la parte del estándar que dice que no puede hacerlo, pero es muy probable que esa porción del estándar se haya escrito de esa manera.
En primer lugar, debe tenerse en cuenta que esto no tiene nada que ver con que el operador se implemente específicamente como amigo . 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 el libro de D & E ( 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 lenguaje también permitía declarar al operador de asignación de copias como una función independiente (no miembro), podría terminar con lo 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 asignación de copia 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 asignación de copias sea declarado como una función independiente.
Es cierto que en su ejemplo particular, que usa específicamente una función amiga , el operador es declarado muy temprano, dentro de la definición de la clase (ya que así es como se declaran los 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.
La intención del 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 donde el LHS es un número entero o algún otro tipo. Ese es un caso manejado por el operator int()
o una función correspondiente del operator T()
. Por lo tanto, el tipo de LHS ya está definido, pero una función de operator=
no miembro podría violar esto.
Por lo tanto, se evita.
Porque hay algunos operadores que DEBEN ser miembros. Estos operadores son:
operator[]
operator=
operator()
operator->
y escriba operadores de conversión, como operator int
.
Aunque uno podría ser capaz de 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
Porque ya existe una función implícita de sobrecarga del operador para ''='' en la clase para hacer una 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 de amigo sobrecargado.
operator=
es una función de miembro especial que el compilador proporcionará si no la declara usted mismo. Debido a este estado especial del operator=
tiene sentido ro requerir que sea una función miembro, por lo que no hay posibilidad de que haya un operator=
miembro generado por el compilador operator=
y un operator=
amigo declarado por el usuario operator=
y ninguna posibilidad de elegir entre los dos.
Esta publicación se aplica a C ++ 11
¿Por qué alguien querría un operator=
no miembro operator=
? Bueno, con un operator=
miembro operator=
entonces el siguiente código es posible:
Test const &ref = ( Test() = something );
que crea una referencia colgante. Un operador no miembro arreglaría esto:
Test& operator=(Test &obj1, Test obj2)
porque ahora la Test()
prvalue Test()
no podrá 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 solo puede invocarse en lvalues, por lo que podría lograr el mismo objetivo escribiendo la función de miembro:
Test &operator=(Test obj2) &
// ^^^
Ahora el código anterior con referencia colgante no se compilará.
NÓTESE BIEN. operator=
debe tomar el lado derecho por valor o referencia constante. Tomar por valor es útil cuando se implementa la expresión copiar y cambiar , 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).