指向函数签名中 const 的智能指针

smart pointer to const in function's signature

我想知道在将 shared_ptr < T> 作为参数传递给采用 shared_ptr < const T> 的函数时,隐式转换是否涉及一些隐藏成本(例如构造一个额外的副本)。

void f(std::shared_ptr<const Widget> ){}

int main(){
   std::shared_ptr<Widget> p; 
   f(p);
   return 0;
}

我假设在这两种情况下我都为引用计数的增加和减少付费。

此外,我想知道如果我使用以下签名定义函数 f(),为什么代码无法编译:

void f(shared_ptr<const Widget>& ){}

更让我感到惊奇的是,事实确实如此:

void f(const shared_ptr<const Widget>& ){}

为什么值传递有效?

您的代码之所以有效,是因为 smart_ptr constructor overload (9)

template< class Y >
shared_ptr( const shared_ptr<Y>& r ) noexcept;

Constructs a shared_ptr which shares ownership of the object managed by r. If r manages no object, this manages no object too. The template overload doesn't participate in overload resolution if Y is not implicitly convertible to (until C++17)compatible with (since C++17) T*.

为什么当方法需要 shared_ptr<const Widget>& 时它不编译?

如果您将签名更改为

void f(shared_ptr<const Widget>& ){}

您不能再一步完成转换和传递给方法,因为临时(转换产生的)不能绑定到非常量引用。但是,您仍然可以分两步完成:

int main(){
    std::shared_ptr<Widget> p; 
    std::shared_ptr<const Widget> p2{p};
    // f(p);  // error: cannot bind non-const reference to temporary
    f(p2);    // OK
    return 0;
}

有开销吗?

关于开销:是的,有一个 smart_ptr<const Widget> 正在构造,然后传递给该方法(就像它在上面的片段中明确显示的那样)。

为什么当该方法需要 const shared_ptr<const Widget>& 时它再次起作用?

关于您的编辑,如果您将签名更改为这样,为什么它会再次起作用:

void f(const shared_ptr<const Widget>& ){}

在这种情况下,如果您传递 shared_ptr<Widget>,仍然会发生转换。但是,现在允许将转换产生的临时值绑定到 const 引用。无论如何,该方法不允许对其进行修改,因此允许传递临时值没有危险。

又一个例子

请注意,临时对象不绑定到非常量引用是 C++ 的一种罕见情况,可帮助您避免愚蠢的错误。考虑一下:

void foo(int& x) { x += 2; }
int bar() { return 3; }

int main() { foo(bar()); }   // error !

将右值传递给需要非常量左值引用的函数没有多大意义。您将无法观察 foobar 返回的值所做的更改。

传递智能指针@cpp 核心指南

关于将智能指针传递给函数,请注意 cpp coreguidelines 中有一些内容。底线是:如果该方法不参与引用计数(可能是最常见的情况),则不要传递智能指针,而是传递原始指针。