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


91

#include "logic.h"
...

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

逻辑文件

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

当我编译时,它说:

std :: ostream&逻辑:: operator <<(std :: ostream&,A&)'必须仅接受一个参数。

问题是什么?

Answers:


127

问题是您在类中定义了它,

a)表示第二个参数是隐式(this),

b)它不会做您想做的事,即extend std::ostream

您必须将其定义为自由函数:

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

8
另外,他将其声明为朋友函数,并将其定义为成员函数。
asaelr 2012年

en.cppreference.com/w/cpp/language/operators中所述,“将std :: istream&或std :: ostream&作为左侧参数的operator >>和operator <<的重载称为插入和由于它们将用户定义的类型作为正确的参数(a @ b中的b),因此必须将其实现为非成员”。
Morteza

49

朋友函数不是成员函数,因此问题在于您声明operator<<为的朋友A

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

然后尝试将其定义为类的成员函数 logic

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

您对logic类还是名称空间感到困惑?

该错误是因为您试图定义一个带有operator<<两个参数的成员,这意味着它需要三个参数,包括隐式this参数。运算符只能使用两个参数,因此在编写a << b两个参数时,它们是ab

您想将其定义为非成员函数,绝对不要将其定义ostream& operator<<(ostream&, const A&)为成员,因为它与该类无关!logic

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

3

我在使用模板化类时遇到了这个问题。这是我不得不使用的更通用的解决方案:

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

现在:*如果要在cpp中隐藏我的toString()函数,则不能将其内联。*您在标头中停留了一些代码,我无法摆脱它。*运算符将调用toString()方法,但未内联。

运算符<<的主体可以在friend子句中或在类外部声明。两种选择都很丑陋。:(

也许我误会或遗漏了一些东西,但是只是向前声明操作符模板在gcc中没有链接。

这也适用:

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

我认为,如果您使用未模板化以实现operator <<的父类,并使用虚拟toString()方法,则也可以避免强制在标头中声明的模板问题。


0

如果将其定义operator<<为成员函数,则与使用non-member相比,它将具有不同的分解语法operator<<。非成员operator<<是二进制运算符,其中成员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;
}

那么...你怎么称呼他们?运算符在某些方面很奇怪,我将挑战您operator<<(...)在头脑中写下语法以使事情变得有意义。

MyObj mo;

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

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

或者,您可以尝试调用非成员二进制运算符:

MyObj mo;

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

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

当您将这些运算符变成成员函数时,您没有义务使它们直观地表现出来,如果需要,您可以定义operator<<(int)左移一些成员变量,以了解人们可能会措手不及,无论您有多少评论写。

几乎最后,有时操作员调用的两个分解都有效,您在这里可能会遇到麻烦,我们将推迟该对话。

最后,请注意编写一个看起来像二进制运算符的一元成员运算符可能会很奇怪(因为您可以使成员运算符成为虚拟的.....也尝试不放行并沿此路径运行.... )

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

    int value = 8;
};

这种语法现在会激怒许多编码人员。

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

请注意cout,这里链中的第二个参数的位置如何。...奇怪吗?

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.