按值返回的临时shared_ptr的计数器是否递增?

Does a temporary shared_ptr returned by value have the counter incremented?

提问人:Mix Kira 提问时间:6/7/2023 最后编辑:user17732522Mix Kira 更新时间:6/7/2023 访问量:97

问:

我试图了解 RVO 在这种特殊情况下如何协同工作。shared_ptr

假设我有这个示例代码:

class A {
public:
  void action() {}
};

class Container {
public:
  shared_ptr<A> getA() { return m_a; }

private:
  shared_ptr<A> m_a;
};

Container cont{};
cont.getA()->action();

如果我没记错的话,在这种情况下,返回的 by 不应该被复制/复制构造,因为它是由编译器优化的。 那么,在最后一行中,我调用函数的函数应该直接包含在对象内部的函数吗?shared_ptrgetA()shared_ptraction()m_aContainer

在这种情况下,如果指针没有被复制,内部引用计数是否不会递增/递减?

而且因为我将它用作 r 值,所以它经过优化,我可以直接使用它来访问指向对象,而无需任何成本,就像原始指针一样?

如果不是这样,有没有办法避免增加/减少的成本?我实际上并没有保留 ,但我仅将其用于对包含的对象进行操作。shared_ptr

或者,所包含对象的生存期可能存在任何问题?

C++ shared-ptr 返回值优化

评论

2赞 Remy Lebeau 6/7/2023
您始终可以用自定义的类替换它,该类记录其实例和复制/移动操作,然后您可以确切地看到编译器实际执行的操作。shared_ptr
2赞 463035818_is_not_an_ai 6/7/2023
a是股份所有权。如果不想共享所有权,可以返回原始指针或引用shared_ptr
0赞 Mix Kira 6/7/2023
@463035818_is_not_a_number,如果我两者都想要?也许在某些情况下我需要共享所有权,而在其他情况下,我只需要对指向的对象进行操作......我必须为不同的 getter 创建吗?
0赞 463035818_is_not_an_ai 6/7/2023
我不会太担心复制共享的 ptr。如果确实在做某事,那么增加和减少引用计数并制作副本对性能来说可能无关紧要。也许不是,但你只能通过编写代码和测量来知道action
2赞 Sebastian 6/7/2023
在大多数(不是全部)情况下,使用糟糕的设计(恕我直言)——没有明确的所有权、明确的生命周期、可能的周期、运行时开销,但这始终取决于要求。对于某些语言,共享指针是默认的。shared_ptr

答:

7赞 Ted Lyngmo 6/7/2023 #1

getA() 返回的shared_ptr不应被复制/复制构造,因为它是由编译器优化的。

是的,它将被复制。 不是局部变量,因此无法进行NRVO(命名返回值优化),并且按值返回它确实会复制构造返回的。m_ashared_ptr<A>

演示

评论

0赞 Mix Kira 6/7/2023
谢谢,我错过了它只能使用局部变量的事实,并希望在这种情况下,我没有保留该值的编译器会直接指向类的成员作为优化。
1赞 Ted Lyngmo 6/7/2023
@MixKira 不客气!(N)RVO 所做的是直接在调用站点创建对象,因此该函数需要实际创建一个对象。另请注意,函数参数不计算在内,也不能在 NRVO 中使用。
3赞 463035818_is_not_an_ai 6/7/2023 #2

简而言之。。。

#include <iostream>

struct foo {
    foo() = default;
    foo(const foo&) { std::cout << "copy\n";}
};

foo bar() {
    foo f;
    return f;
}

int main() {
    foo g;
    g = bar();
}

在不省略复制的情况下:你调用函数,在函数里面有 ,函数返回,被复制到返回的值中,被销毁,你现在有 ,一个 的副本。fffgf

省略副本的想法是:将副本放在一边的一小段时间,永远不会超过 1 个对象。不制作副本的唯一效果是不制作副本,即可以跳过副作用,并且上面的代码不会打印任何内容。

就您而言,这不适用。 是会员。它不是函数局部变量。必须创建副本,因为从函数返回的对象与成员不同。m_a

现场演示

禁用复制省略的现场演示

是的,从函数返回的临时是副本。共享指针的引用计数在完整表达式的末尾递减。

评论

0赞 Mix Kira 6/7/2023
谢谢,现在更清楚了......根据我对 RVO 的理解,它是一个排序别名,因此我认为 NRVO 也可以应用于返回的类成员,因为它编译器知道它在内存中的位置(与对象相关),并且可以指向内存中的同一对象。
2赞 463035818_is_not_an_ai 6/7/2023
@MixKira那将是.复制省略不等同于返回引用shared_ptr<A>& getA() { return m_a; }