“C++中在cout中调用函数会导致“必须调用非静态成员函数的引用”错误。”

huangapple go评论66阅读模式
英文:

C++ function call in cout leads to "Reference to non-static member function must be called"

问题

以下是翻译好的部分:

我想直接将一些属性打印到 cout。幸运的是,我找到了一种针对静态调用的解决方案,就像这样:

// 某处:
ostream & Foo(ostream &ostr){
    ostr << "Foo";
    return ostr;
}

还有另一个静态调用,可以像这样直接调用:

// 其他地方:
void doSomething(){
    cout << 123 << Foo << endl;
}

这很好地工作,并且简单地打印出 "123Foo"。但是,我想将其用于我拥有的对象,以便打印其属性。在我的类 A 中,我有一个类 B 的属性 b。在那里,函数的实现类似于之前的静态函数:

ostream & Foo(ostream &ostr) {
    ostr << "my foo is 123";
    return ostr;
}

但是它无法编译。我还尝试指定命名空间,例如:

ostream & B::Foo(ostream &ostr) { ... }

然后假设我可以像这样直接调用对象的函数:

cout << 123 << b->Foo << endl;

但它总是告诉我有一个错误:"对非静态成员函数的引用必须调用"。

我就是无法搞定这个。我做错了什么?这是否可能?

注意:我不想重载 << 运算符或其他任何内容。我不想要一个 "toString()" 方法或类似的东西,但我想了解为什么这个实例方法不能像其他地方一样被调用。作为一个 Java 开发者,我本来以为像 "My object " + b.foo() 这样的东西是可能的。但似乎是不行的。

英文:

I wanted to print some properties directly to cout. Luckily I found a solution for static calls like this:

// somewhere:
ostream &amp; Foo(ostream &amp;ostr){
    ostr &lt;&lt; &quot;Foo&quot;;
    return ostr;
}

and another static call where it can be called directly like that:

// somoewhere else:
void doSomething(){
    cout &lt;&lt; 123 &lt;&lt; Foo &lt;&lt; endl;
}

This works fine and prints simply "123Foo". But I want to use this for an object I have, so I could print its properties. In my class A I have a property b of the class B. There the function is implemented similar to the static one before:

ostream &amp; Foo(ostream &amp;ostr) {
    ostr &lt;&lt; &quot;my foo is 123&quot;;
    return ostr;
}

but it doesn't compile. I also tried to specify the namespace like

ostream &amp; B::Foo(ostream &amp;ostr) { ... }

and assumed I could just call the object's function like this:

cout &lt;&lt; 123 &lt;&lt; b-&gt;Foo &lt;&lt; endl;

but it always tells me there is an error: "Reference to non-static member function must be called".

I just can't get this done. What am I doing wrong? Is this even possible?

NOTE: I don't want to overload the << operator or anything. I don't want a "toString()" method or anything similar but would like to understand why this instance method can not be called like anywhere else.
As a Java developer I would have guessed something like "My object " + b.foo() would be possible. But it seems, it is not.

答案1

得分: 0

问题在于你的独立函数 foo 是一个函数,而 b->foo 在C++中不是一个函数。换句话说,后者绑定了它的 this 参数,所以除了函数的行为之外,它还需要一些额外的数据。你可以使用显式lambda函数使其成为有效的 std::function

cout << 123 << [&b_object](ostream& out) { return b_object->foo(out); } << endl;

如果这是你对这个函数唯一的预期用法,那么你可以直接从 B::Foo 返回它。

std::function<ostream&(ostream&)> B::foo() {
  return [&this](ostream& out) { out << "my foo is " << my_foo; return out; };
}

然后你可以这样写:

cout << 123 << b_object->foo() << endl;

但是,正如评论中所指出的,更常见的做法是使用专门的数据类型。如果你要打印的属性很简单,那么只需从 foo() 返回它或者它的引用即可。如果不是,或者如果你希望进行一些复杂的自定义打印操作,我建议编写自己的自定义类,并封装这个自定义打印行为。

class ViewOfObjectB {
private:
  B& owner;
public:
  ViewOfObjectB(B& owner) : owner(owner) {}
  friend ostream& operator<<(ostream&, const ViewOfObjectB&);
};

ostream& operator<<(ostream& out, ViewOfObjectB& obj) {
  // 你想要打印的内容放在这里...
  return out;
}

class B {
public:
  ViewOfObjectB foo() {
    return ViewOfObjectB(&this);
  }
};

与使用lambda函数的思路相同,但由于有大量的隐藏复杂性,我们使用了一个具有命名的视图类,可以在代码增长并变得更加复杂时独立地记录这个复杂性。你仍然可以这样调用它:

cout << 123 << b_object->foo() << endl;

所有这些调用都是静态的(在"没有虚函数表涉及"的意义上,而不是 static 关键字的意义上),所以一个聪明的优化器应该能够为你将所有内容内联。

英文:

The problem is that your standalone foo is a function, whereas b-&gt;foo is, according to C++, not one. Namely, the latter binds its this argument, so it requires some extra data on top of just the function's behavior. You can make it a valid std::function with an explicit lambda.

cout &lt;&lt; 123 &lt;&lt; [&amp;b_object](ostream&amp; out) { return b_object-&gt;foo(out); } &lt;&lt; endl;

and if that's your only intended use case for this function, then you can return it from B::Foo directly.

// Be careful with lifetimes! The return value cannot
// outlive `this`!
std::function&lt;ostream&amp;(ostream&amp;)&gt; B::foo() {
  return [&amp;this](ostream&amp; out) { out &lt;&lt; &quot;my foo is &quot; &lt;&lt; my_foo; return out; };
}

Then you can write

cout &lt;&lt; 123 &lt;&lt; b_object-&gt;foo() &lt;&lt; endl;

But, as indicated in the comments, it's much more common to use a specialized datatype for this. If the property you're trying to print is cheap, then just return it or a reference to it from foo() and you're done. If not, or if you want some complicated custom printing operation to happen, I recommend writing your own custom class and encapsulating this custom print behavior.

class ViewOfObjectB {
private:
  B&amp; owner;
public:
  ViewOfObjectB(B&amp; owner) : owner(owner) {}
  friend ostream&amp; operator&lt;&lt;(ostream&amp;, const ViewOfObjectB&amp;);
};

ostream&amp; operator&lt;&lt;(ostream&amp; out, ViewOfObjectB&amp; obj) {
  // Whatever you want to print goes here ...
  return out;
}

class B {
public:
  ViewOfObjectB foo() {
    return ViewOfObjectB(&amp;this);
  }
};

Same idea as using a lambda, but since there's a ton of hidden complexity, we make that complexity explicit with a named view class that we can document independently as our code grows and becomes more complicated. You still call it using

cout &lt;&lt; 123 &lt;&lt; b_object-&gt;foo() &lt;&lt; endl;

All of these calls are static (in the sense of "no vtable involved", not in the sense of the static keyword), so a clever optimizer should be able to inline it all for you.

huangapple
  • 本文由 发表于 2023年5月15日 04:35:45
  • 转载请务必保留本文链接:https://go.coder-hub.com/76249568.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定