operador << debe tomar exactamente un argumento

94

ah

#include "logic.h"
...

class A
{
friend ostream& operator<<(ostream&, A&);
...
};

logic.cpp

#include "a.h"
...
ostream& logic::operator<<(ostream& os, A& a)
{
...
}
...

Cuando compilo, dice:

std :: ostream & logic :: operator << (std :: ostream &, A &) 'debe tomar exactamente un argumento.

¿Cuál es el problema?

Como como
fuente

Respuestas:

132

El problema es que lo define dentro de la clase, que

a) significa que el segundo argumento es implícito ( this) y

b) no hará lo que usted quiere que haga, es decir, extender std::ostream.

Tienes que definirlo como una función libre:

class A { /* ... */ };
std::ostream& operator<<(std::ostream&, const A& a);
Gato Plus Plus
fuente
9
Además, la declara como función amiga y la define como función miembro.
asaelr
Como se menciona en en.cppreference.com/w/cpp/language/operators , "las sobrecargas de operator >> y operator << que toman un std :: istream & o std :: ostream & como el argumento de la izquierda se conocen como inserción y operadores de extracción. Dado que toman el tipo definido por el usuario como el argumento correcto (b en a @ b), deben implementarse como no miembros ".
Morteza
49

Una función de amigo no es una función miembro, por lo que el problema es que declaras operator<<como amigo de A:

 friend ostream& operator<<(ostream&, A&);

luego intente definirlo como una función miembro de la clase logic

 ostream& logic::operator<<(ostream& os, A& a)
          ^^^^^^^

¿Está confundido acerca de si logices una clase o un espacio de nombres?

El error se debe a que ha intentado definir un miembro operator<<tomando dos argumentos, lo que significa que toma tres argumentos, incluido el thisparámetro implícito . El operador solo puede tomar dos argumentos, de modo que cuando escribe a << blos dos argumentos son ayb .

Se desea definir ostream& operator<<(ostream&, const A&)como no la función -miembro, definitivamente no como miembro de logicpuesto que no tiene nada que ver con esa clase!

std::ostream& operator<<(std::ostream& os, const A& a)
{
  return os << a.number;
}
Jonathan Wakely
fuente
3

Me encontré con este problema con las clases basadas en plantillas. Aquí hay una solución más general que tuve que usar:

template class <T>
class myClass
{
    int myField;

    // Helper function accessing my fields
    void toString(std::ostream&) const;

    // Friend means operator<< can use private variables
    // It needs to be declared as a template, but T is taken
    template <class U>
    friend std::ostream& operator<<(std::ostream&, const myClass<U> &);
}

// Operator is a non-member and global, so it's not myClass<U>::operator<<()
// Because of how C++ implements templates the function must be
// fully declared in the header for the linker to resolve it :(
template <class U>
std::ostream& operator<<(std::ostream& os, const myClass<U> & obj)
{
  obj.toString(os);
  return os;
}

Ahora: * Mi función toString () no puede estar en línea si va a estar escondida en cpp. * Estás atascado con un código en el encabezado, no pude deshacerme de él. * El operador llamará al método toString (), no está en línea.

El cuerpo del operador << se puede declarar en la cláusula friend o fuera de la clase. Ambas opciones son feas. :(

Tal vez estoy entendiendo mal o me estoy perdiendo algo, pero simplemente declarar que la plantilla del operador no se vincula en gcc.

Esto también funciona:

template class <T>
class myClass
{
    int myField;

    // Helper function accessing my fields
    void toString(std::ostream&) const;

    // For some reason this requires using T, and not U as above
    friend std::ostream& operator<<(std::ostream&, const myClass<T> &)
    {
        obj.toString(os);
        return os;
    }
}

Creo que también puede evitar los problemas de plantillas que obligan a las declaraciones en los encabezados, si usa una clase principal que no tiene una plantilla para implementar el operador <<, y usa un método toString () virtual.

Dan Truong
fuente
0

Si la define operator<<como función miembro, tendrá una sintaxis descompuesta diferente a la que tendría si usara una función no miembro operator<<. Un no miembro operator<<es un operador binario, donde un miembro operator<<es un operador unario.

// Declarations
struct MyObj;
std::ostream& operator<<(std::ostream& os, const MyObj& myObj);

struct MyObj
{
    // This is a member unary-operator, hence one argument
    MyObj& operator<<(std::ostream& os) { os << *this; return *this; }

    int value = 8;
};

// This is a non-member binary-operator, 2 arguments
std::ostream& operator<<(std::ostream& os, const MyObj& myObj)
{
    return os << myObj.value;
}

Entonces ... ¿cómo los llamas realmente? Los operadores son extraños de alguna manera, te desafiaré a que escribas la operator<<(...)sintaxis en tu cabeza para que las cosas tengan sentido.

MyObj mo;

// Calling the unary operator
mo << std::cout;

// which decomposes to...
mo.operator<<(std::cout);

O puede intentar llamar al operador binario que no es miembro:

MyObj mo;

// Calling the binary operator
std::cout << mo;

// which decomposes to...
operator<<(std::cout, mo);

No tiene la obligación de hacer que estos operadores se comporten de manera intuitiva cuando los convierte en funciones miembro, puede definir operator<<(int) desplazamiento a la izquierda de alguna variable miembro si lo desea, comprenda que las personas pueden estar un poco desprevenidas, sin importar cuántos comentarios pueda hacer escribir.

Casi por último, puede haber ocasiones en las que ambas descomposiciones para una llamada de operador sean válidas, puede tener problemas aquí y aplazaremos esa conversación.

Por último, tenga en cuenta lo extraño que podría ser escribir un operador de miembro unario que se supone que debe verse como un operador binario (ya que puede hacer que los operadores de miembro sean virtuales ..... también intentando no delegar y ejecutar esta ruta ... )

struct MyObj
{
    // Note that we now return the ostream
    std::ostream& operator<<(std::ostream& os) { os << *this; return os; }

    int value = 8;
};

Esta sintaxis irritará a muchos programadores ahora ...

MyObj mo;

mo << std::cout << "Words words words";

// this decomposes to...
mo.operator<<(std::cout) << "Words words words";

// ... or even further ...
operator<<(mo.operator<<(std::cout), "Words words words");

Observe cómo coutes el segundo argumento de la cadena aquí ... ¿extraño verdad?

Rinzler
fuente