运算符 < < 必须只接受一个参数

#include "logic.h"
...


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

Logic.cpp

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

当我编译的时候,它说:

Ostream & logic: : operation < < (std: : ostream & ,A &)’必须只接受一个参数。

有什么问题吗?

126962 次浏览

The problem is that you define it inside the class, which

a) means the second argument is implicit (this) and

b) it will not do what you want it do, namely extend std::ostream.

You have to define it as a free function:

class A { /* ... */ };
std::ostream& operator<<(std::ostream&, const A& a);

A friend function is not a member function, so the problem is that you declare operator<< as a friend of A:

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

then try to define it as a member function of the class logic

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

Are you confused about whether logic is a class or a namespace?

The error is because you've tried to define a member operator<< taking two arguments, which means it takes three arguments including the implicit this parameter. The operator can only take two arguments, so that when you write a << b the two arguments are a and b.

You want to define ostream& operator<<(ostream&, const A&) as a non-member function, definitely not as a member of logic since it has nothing to do with that class!

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

I ran into this problem with templated classes. Here's a more general solution I had to use:

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

Now: * My toString() function can't be inline if it is going to be tucked away in cpp. * You're stuck with some code in the header, I couldn't get rid of it. * The operator will call the toString() method, it's not inlined.

The body of operator<< can be declared in the friend clause or outside the class. Both options are ugly. :(

Maybe I'm misunderstanding or missing something, but just forward-declaring the operator template doesn't link in gcc.

This works too:

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

I think you can also avoid the templating issues forcing declarations in headers, if you use a parent class that is not templated to implement operator<<, and use a virtual toString() method.

If you define operator<< as a member function it will have a different decomposed syntax than if you used a non-member operator<<. A non-member operator<< is a binary operator, where a member operator<< is a unary operator.

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

So.... how do you really call them? Operators are odd in some ways, I'll challenge you to write the operator<<(...) syntax in your head to make things make sense.

MyObj mo;


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


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

Or you could attempt to call the non-member binary operator:

MyObj mo;


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


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

You have no obligation to make these operators behave intuitively when you make them into member functions, you could define operator<<(int) to left shift some member variable if you wanted to, understand that people may be a bit caught off guard, no matter how many comments you may write.

Almost lastly, there may be times where both decompositions for an operator call are valid, you may get into trouble here and we'll defer that conversation.

Lastly, note how odd it might be to write a unary member operator that is supposed to look like a binary operator (as you can make member operators virtual..... also attempting to not devolve and run down this path....)

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


int value = 8;
};

This syntax will irritate many coders now....

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");

Note how the cout is the second argument in the chain here.... odd right?

Operator overloading includes member function overloading and non-member function overloading, which cannot be mixed. https://condor.depaul.edu/ntomuro/courses/262/notes/lecture3.html

The key point is the logic:: before operator<< which is defined as a friend function.

logic:: is only added before the member function. I understand that this is similar to telling the compiler that this function is a member function and granting it corresponding permissions (such as accessing private functions).

In other words, just as @asaelr and @Morteza mentioned, "when defining a friend function you do not use the name of the class to scope the name of the friend function".

Hence, we should remove logic:: before operator<<.