¿Por qué los compiladores de C++ no definen operador== y operador!=?

Resuelto Rob asked hace 15 años • 13 respuestas

Soy un gran admirador de dejar que el compilador haga todo el trabajo posible por usted. Al escribir una clase simple, el compilador puede brindarle lo siguiente de forma "gratuita":

  • Un constructor predeterminado (vacío)
  • Un constructor de copiar y mover.
  • un destructor
  • Operadores de Asignación ( operator=)

Pero parece que no puede ofrecerle ningún operador de comparación, como operator==o operator!=. Por ejemplo:

class foo
{
public:
    std::string str_;
    int n_;
};

foo f1;        // Works
foo f2(f1);    // Works
foo f3;
f3 = f2;       // Works

if (f3 == f2)  // Fails
{ }

if (f3 != f2)  // Fails
{ }

¿Hay alguna buena razón para esto? ¿Por qué sería un problema realizar una comparación miembro por miembro? Obviamente, si la clase asigna memoria, entonces querrás tener cuidado, pero para una clase simple seguramente el compilador podría hacer esto por ti.

Rob avatar Oct 20 '08 16:10 Rob
Aceptado

El argumento de que si el compilador puede proporcionar un constructor de copia predeterminado, debería poder proporcionar un valor predeterminado similar operator==()tiene cierto sentido. Creo que el motivo de la decisión de no proporcionar un valor predeterminado generado por el compilador para este operador se puede adivinar por lo que dijo Stroustrup sobre el constructor de copia predeterminado en "El diseño y la evolución de C++" (Sección 11.4.1 - Control de copia). :

Personalmente considero desafortunado que las operaciones de copia estén definidas de forma predeterminada y prohíbo la copia de objetos de muchas de mis clases. Sin embargo, C++ heredó sus constructores de copia y asignación predeterminados de C, y se utilizan con frecuencia.

Entonces, en lugar de "¿por qué C++ no tiene un valor predeterminado operator==()?", la pregunta debería haber sido "¿por qué C++ tiene una asignación predeterminada y un constructor de copia?", y la respuesta fue que Stroustrup incluyó esos elementos a regañadientes por compatibilidad con versiones anteriores de C. (probablemente la causa de la mayoría de los defectos de C++, pero también probablemente la razón principal de la popularidad de C++).

Para mis propios fines, en mi IDE, el fragmento que uso para las nuevas clases contiene declaraciones para un operador de asignación privado y un constructor de copia, de modo que cuando genero una nueva clase no obtengo operaciones de copia y asignación predeterminadas; tengo que eliminar explícitamente la declaración. de esas operaciones de la private:sección si quiero que el compilador pueda generarlas por mí.

Michael Burr avatar Oct 20 '2008 14:10 Michael Burr

Incluso en C++ 20, el compilador aún no generará operator==implícitamente

struct foo
{
    std::string str;
    int n;
};

assert(foo{"Anton", 1} == foo{"Anton", 1}); // ill-formed

Pero obtendrá la capacidad de establecer valores predeterminados explícitamente == desde C++ 20 :

struct foo
{
    std::string str;
    int n;

    // either member form
    bool operator==(foo const&) const = default;
    // ... or friend form
    friend bool operator==(foo const&, foo const&) = default;
};

El valor predeterminado ==se realiza por miembros ==(de la misma manera que el constructor de copia predeterminado realiza la construcción de copias por miembros). Las nuevas reglas también proporcionan la relación esperada entre ==y !=. Por ejemplo, con la declaración anterior, puedo escribir ambos:

assert(foo{"Anton", 1} == foo{"Anton", 1}); // ok!
assert(foo{"Anton", 1} != foo{"Anton", 2}); // ok!

Esta característica específica (incumplimiento operator==y simetría entre ==y !=) proviene de una propuesta que era parte de la característica más amplia del lenguaje que es operator<=>.

Anton Savin avatar Jan 08 '2015 10:01 Anton Savin

El compilador no sabría si desea una comparación de punteros o una comparación profunda (interna).

Es más seguro simplemente no implementarlo y dejar que el programador lo haga él mismo. Luego podrán hacer todas las suposiciones que quieran.

Mark Ingram avatar Oct 20 '2008 09:10 Mark Ingram

En mi humilde opinión, no existe una "buena" razón. La razón por la que hay tanta gente que está de acuerdo con esta decisión de diseño es porque no aprendieron a dominar el poder de la semántica basada en valores. La gente necesita escribir muchos constructores de copias, operadores de comparación y destructores personalizados porque utilizan punteros sin formato en su implementación.

Cuando se utilizan punteros inteligentes apropiados (como std::shared_ptr), el constructor de copia predeterminado suele estar bien y la implementación obvia del hipotético operador de comparación predeterminado sería igual de buena.

alexk7 avatar Apr 03 '2009 17:04 alexk7