指向函数签名中 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 !
将右值传递给需要非常量左值引用的函数没有多大意义。您将无法观察 foo
对 bar
返回的值所做的更改。
传递智能指针@cpp 核心指南
关于将智能指针传递给函数,请注意 cpp coreguidelines 中有一些内容。底线是:如果该方法不参与引用计数(可能是最常见的情况),则不要传递智能指针,而是传递原始指针。
我想知道在将 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 !
将右值传递给需要非常量左值引用的函数没有多大意义。您将无法观察 foo
对 bar
返回的值所做的更改。
传递智能指针@cpp 核心指南
关于将智能指针传递给函数,请注意 cpp coreguidelines 中有一些内容。底线是:如果该方法不参与引用计数(可能是最常见的情况),则不要传递智能指针,而是传递原始指针。