【问题标题】:const <type>& foo() versus <type> foo()const <type>& foo() 与 <type> foo()
【发布时间】:2010-01-12 06:39:35
【问题描述】:

非模板类中,有什么理由更喜欢const &lt;type&gt;&amp; foo(); 形式的函数返回签名而不是&lt;type&gt; foo();?其中&lt;type&gt; 是一个内在类型。如果&lt;type&gt; 是一个类/结构对象呢?

还对 const 的函数是否会产生影响感兴趣:const &lt;type&gt;&amp; foo() const;&lt;type&gt; foo() const;

例如在非模板类,非模板函数中:

const int& foo() const { return member_variable_; }

对比

int foo() const { return member_variable_; }

【问题讨论】:

标签: c++ optimization visual-c++ g++


【解决方案1】:

对于原始类型,只需按值返回。没有理由通过 const 引用返回原语。

对于非原始类型,视情况而定。

如果返回值是函数的局部变量,则必须按值返回。否则,在调用者可以使用返回的引用之前,变量将被销毁。

如果返回的值是类数据成员,那么您可以选择。

通过 const 引用返回可避免复制,这可能是性能优势。但是,它将您的接口与实现联系起来,即数据成员和返回值必须是相同的类型。它也不太安全,因为调用者可以保留引用比对象的生命周期更长,例如:

const X& x = y->foo();
delete y;
// use x, oops!

按值返回会产生一个或两个副本,这取决于您是否可以利用返回值优化(这可能适用于诸如此类的简单 get() 方法)。此外,使用默认复制构造函数的对象副本可以更有效地复制(想想:编译器可以简单地 memcpy 数据)。

建议:从按值返回开始,只有在确定调用/复制已知是性能问题后才切换到按常量引用返回。

最后,另一种选择是通过参数返回,这对于更复杂的对象(例如,字符串)很有用:

void foo(X& return_val) { return_val = member_variable_; }

或:

void foo(X* return_val) {
    assert(return_val != 0);
    *return_val = member_variable_;
}

【讨论】:

  • '使用默认的对象副本 ... memcpy' => 不完全。该标准允许编译器在所谓的返回值优化中省略副本。 T f() { T tmp; /* operate */ return tmp; } 允许编译器做的是在为返回值分配的内存中创建 tmp 变量,因此避免在返回语句中复制变量。编译器不会将memcpy 对象从一个位置转移到另一个位置。
  • 我的 memcpy 评论不是指返回值优化,而是指 POD 对象的直接副本与间接副本。例如,请参阅docs.sun.com/app/docs/doc/820-7599/bkahu?a=view
【解决方案2】:

如果您需要返回一个值,则必须返回一个值 - const 引用不是可接受的替代品。但我怀疑你问的是这样的事情:

struct A {
   X x;
    ...
   X f1() { return x; }
   const X & f2() { return x; }
};

我从来没有为此想出一个万无一失的指导方针。

【讨论】:

  • @Neil:对内在类型更感兴趣,在您的示例中,X 将是一个整数。 (对于它的价值,我更喜欢按价值返回)
  • 我认为,使用auto,通过引用返回变得越来越危险。现在像auto x = a.f2() 这样的东西会导致x 悄悄地成为一个引用,如果ax 之前被销毁(如果它实际上是一个智能指针,这很有可能),我们得到一个悬空引用。 IMO,除非打算返回一个引用(最好通过返回一个 [可能是智能的] 指针来突出显示),只需按值返回,然后让 RVO 启动。
  • 哦,是的,另外,在 C++0x 中使用移动语义,对于任何具有移动构造函数的类型(它涵盖所有标准类型,并且为任何复制成本高的用户定义类型定义一个肯定是最佳实践。
  • @Pavel 我几乎总是自己返回值,但偶尔会觉得有点难以证明——你的观点很好,我同意“总是返回值”很好(而且很简单)规则。
  • @ceretullis 对于内置类型,我看不出不返回值的任何可能理由 - 这样做没有效率问题(相反,如果有的话)。
【解决方案3】:
const <type>& foo();

您的类中可能有一个资源,例如元素容器,foo() 返回特定元素,如果您知道资源将可用足够长的时间,您可能会返回对容器中元素的 const 引用,这样可以避免不必要的分配/数据副本。 它可能是const 或不是,取决于您如何处理数据,如果您不希望通过foo() 返回的引用修改数据,则将其设为常量。也许您有一个想要修改资源并且需要完成一些额外工作的情况,您可以将其设为非 const。您可以在您的 api 中同时拥有两者,用户将根据用例进行选择。

<type> foo();

返回资源&lt;type&gt; 的新副本。这意味着制作了副本。这适用于简单数据或可能共享值的地方,并且您希望确保 &lt;type&gt; 的每个实例都是独立的 - 像某些字符串或 int 一样非共享。

【讨论】:

    【解决方案4】:

    这完全取决于你到底想做什么。

    如果要返回对用户代码要使用的对象的引用,则:

    <type>& foo()  { ... }
    

    如果你想以只读方式返回一个对象的引用,只允许访问该对象类型的 const 函数,那么:

    const <type>& foo() { ... }
    

    如果要返回对用户代码使用的对象的引用,即使类实例以只读方式(作为 const)使用,那么:

    <type>& foo() const { ... }
    

    如果您想以只读方式返回对对象的引用,只允许访问此对象类型的 const 函数,如果类实例以只读方式(作为 const)使用,则允许访问事件:

    const <type>& foo() const { ... }
    

    只是添加一个警告:不要返回对函数变量的引用,它们在函数之外不是活着的......

    【讨论】:

    • 我不得不说我非常同意“也就是说,这种功能的一个很好的做法(但不是在所有情况下)......” - 只有极少数情况下这是一个很好的做法案例 - 容器的 operator[] 是我能想到的唯一例子。
    • 好吧,我会删除这个建议,因为我觉得经验不够,但我不得不说我经常使用与这个方案匹配的类似访问器的成员函数,使用 const-correctness 来制作通过使用非常量成员函数,可以在不破坏整个系统的情况下对整个系统进行只读访问。也就是说,我可能没有足够的经验来发现问题所在。
    • 仅仅因为你有一个类的非常量实例(或引用)并不意味着你应该不受限制地访问它的数据成员。如果你仅仅依赖于你正在访问的实例的 const-ness,你还不如取消成员函数并使用普通结构。
    【解决方案5】:

    如果类型是内在类型,则传递 const refs 不会提高性能,并且可能会降低性能,并且不太安全;最好是按值传递和返回。我不是一个 ASM 人,但据我了解,通过引用返回意味着该值不能存储在寄存器中,因为它需要一个返回地址,因此可以禁用一些其他有用的优化;不过我对此并不积极。

    如果是类或结构,则取决于值的生命周期;如果它是函数本地的,则不能返回,否则,如果它是类成员,我更喜欢 const type& return,因为它可以通过避免复制构造函数来提供更好的性能。 (意见不同,因为按值被认为更安全)。它还使您不必实现复制构造函数或使用潜在的邪恶默认值,这很好。

    【讨论】:

      猜你喜欢
      • 2014-03-20
      • 1970-01-01
      • 1970-01-01
      • 2017-08-14
      • 2011-01-18
      • 1970-01-01
      • 1970-01-01
      • 2019-12-10
      • 2020-03-08
      相关资源
      最近更新 更多