operador << deve ter exatamente um argumento

91

ah

#include "logic.h"
...

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

logic.cpp

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

Quando eu compilo, ele diz:

std :: ostream & logic :: operator << (std :: ostream &, A &) 'deve receber exatamente um argumento.

Qual é o problema?

Como ... como
fonte

Respostas:

127

O problema é que você o define dentro da classe, que

a) significa que o segundo argumento é implícito ( this) e

b) não fará o que você deseja, ou seja, estender std::ostream.

Você deve defini-lo como uma função livre:

class A { /* ... */ };
std::ostream& operator<<(std::ostream&, const A& a);
Cat Plus Plus
fonte
8
Além disso, ele a declara como uma função de amigo e a define como uma função de membro.
asaelr
Conforme mencionado em en.cppreference.com/w/cpp/language/operators , "as sobrecargas de operador >> e operador << que recebem um std :: istream & ou std :: ostream & como o argumento do lado esquerdo são conhecidas como inserção e operadores de extração. Uma vez que tomam o tipo definido pelo usuário como o argumento correto (b em a @ b), eles devem ser implementados como não membros ".
Morteza
49

Uma função de amigo não é uma função de membro, então o problema é que você declara operator<<como amigo de A:

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

em seguida, tente defini-lo como uma função membro da classe logic

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

Você está confuso sobre se logic é uma classe ou um namespace?

O erro é porque você tentou definir um membro operator<<levando dois argumentos, o que significa que leva três argumentos, incluindo o thisparâmetro implícito . O operador só pode aceitar dois argumentos, de modo que, quando você escreve, a << bos dois argumentos são aeb .

Você deseja definir ostream& operator<<(ostream&, const A&)como uma função não- membro, definitivamente não como um membro de, logicuma vez que não tem nada a ver com aquela classe!

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

Corri para esse problema com classes modeladas. Aqui está uma solução mais geral que tive de 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;
}

Agora: * Minha função toString () não pode ser embutida se for colocada em cpp. * Você está preso com algum código no cabeçalho, não consegui me livrar dele. * O operador chamará o método toString (), não é sequencial.

O corpo do operador << pode ser declarado na cláusula friend ou fora da classe. Ambas as opções são feias. :(

Talvez eu esteja entendendo mal ou faltando alguma coisa, mas apenas declarar o modelo de operador não vincula no gcc.

Isso também 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;
    }
}

Eu acho que você também pode evitar os problemas de modelagem que forçam declarações nos cabeçalhos, se você usar uma classe pai que não seja modelada para implementar o operador <<, e usar um método toString () virtual.

Dan Truong
fonte
0

Se você definir operator<< como uma função de membro, ela terá uma sintaxe decomposta diferente do que se você usasse um não membro operator<<. Um não membro operator<<é um operador binário, onde um membro operator<<é um operador unário.

// 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;
}

Então .... como você realmente os chama? Os operadores são estranhos em alguns aspectos, vou desafiá-lo a escrever a operator<<(...)sintaxe em sua cabeça para fazer as coisas fazerem sentido.

MyObj mo;

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

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

Ou você pode tentar chamar o operador binário não membro:

MyObj mo;

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

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

Você não tem obrigação de fazer esses operadores se comportarem intuitivamente ao transformá-los em funções de membro, você pode definir operator<<(int)para o deslocamento à esquerda alguma variável de membro se quiser, entenda que as pessoas podem ser um pouco pegas de surpresa, não importa quantos comentários você possa escrever.

Quase por último, pode haver momentos em que ambas as decomposições para uma chamada de operador sejam válidas, você pode ter problemas aqui e nós adiaremos essa conversa.

Por último, observe como pode ser estranho escrever um operador de membro unário que supostamente se pareça com um operador binário (já que você pode tornar os operadores de membro virtuais ..... também tentando não devolver e percorrer este caminho .... )

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

    int value = 8;
};

Esta sintaxe irritará muitos programadores agora ....

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 como o couté o segundo argumento na cadeia aqui .... estranho, certo?

Rinzler
fonte