你好,我是雨乐~
完美转发是C++中的一种技术,允许函数将参数完美地转发给其他函数,保留其值类别(如左值或右值)。它通常通过std::forward
实现,确保在转发时不会丢失任何信息。
今天,我们并非聊其使用或者机制(相信这些已经有很多优秀的相关文章了),而是聊聊完美转发在某些特定场景下的使用限制。
先看下下面两个类:
struct X{
int a;
int b;
};
structY{
Y(X x){}
};
int main() {
Y y({1, 2});
return0;
}
其中,{1,2}
被推导为class X。
再看下面这段代码:
std::shared_ptr<Y> ptr(new Y({1, 2}));
这种写法也是没问题的,再看下面这种写法:
std::shared_ptr<Y> ptr1 = std::make_shared<Y>({1, 2});
gcc14.2下报错如下:
<source>: In function 'int main()':
<source>:15:48: error: no matching function for call to 'make_shared<Y>(<brace-enclosed initializer list>)'
15 | std::shared_ptr<Y> ptr1 = std::make_shared<Y>({1, 2});
| ~~~~~~~~~~~~~~~~~~~^~~~~~~~
In file included from /opt/compiler-explorer/gcc-14.2.0/include/c++/14.2.0/memory:80,
from <source>:1:
/opt/compiler-explorer/gcc-14.2.0/include/c++/14.2.0/bits/shared_ptr.h:1003:5: note: candidate: 'std::shared_ptr<std::_NonArray<_Tp> > std::make_shared(_Args&& ...) [with _Tp = Y; _Args = {}; _NonArray<_Tp> = Y]'
1003 | make_shared(_Args&&... __args)
| ^~~~~~~~~~~
/opt/compiler-explorer/gcc-14.2.0/include/c++/14.2.0/bits/shared_ptr.h:1003:5: note: candidate expects 0 arguments, 1 provided
// ...
从上面报错信息,需要0个参数,但是提供了1个...
再看看clang的报错,如下:
<source>:15:29: error: no matching function for call to 'make_shared'
15 | std::shared_ptr<Y> ptr1 = std::make_shared<Y>({1, 2});
| ^~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/gcc-13.2.0/lib/gcc/x86_64-linux-gnu/13.2.0/../../../../include/c++/13.2.0/bits/shared_ptr.h:1005:5: note: candidate template ignored: substitution failure [with _Tp = Y]: deduced incomplete pack <(no value)> fortemplate parameter '_Args'
1003 | template<typename _Tp, typename... _Args>
| ~~~~~
1004 | inline shared_ptr<_NonArray<_Tp>>
1005 | make_shared(_Args&&... __args)
| ^
/opt/compiler-explorer/gcc-13.2.0/lib/gcc/x86_64-linux-gnu/13.2.0/../../../../include/c++/13.2.0/bits/shared_ptr.h:1037:5: note: candidate template ignored: substitution failure [with _Tp = Y]: constraints not satisfied for alias template'_UnboundedArray' [with _Tp = Y]
1036 | inline shared_ptr<_UnboundedArray<_Tp>>
| ~~~~~~~~~~
1037 | make_shared(size_t __n)
| ^
/opt/compiler-explorer/gcc-13.2.0/lib/gcc/x86_64-linux-gnu/13.2.0/../../../../include/c++/13.2.0/bits/shared_ptr.h:1095:5: note: candidate template ignored: substitution failure [with _Tp = Y]: constraints not satisfied for alias template'_BoundedArray' [with _Tp = Y]
1094 | inline shared_ptr<_BoundedArray<_Tp>>
| ~~~~~~~~~~
1095 | make_shared(constremove_extent_t<_Tp>& __u)
| ^
/opt/compiler-explorer/gcc-13.2.0/lib/gcc/x86_64-linux-gnu/13.2.0/../../../../include/c++/13.2.0/bits/shared_ptr.h:1080:5: note: candidate function templatenot viable: requires0 arguments, but 1 was provided
1080 | make_shared()
| ^
/opt/compiler-explorer/gcc-13.2.0/lib/gcc/x86_64-linux-gnu/13.2.0/../../../../include/c++/13.2.0/bits/shared_ptr.h:1053:5: note: candidate function templatenot viable: requires2 arguments, but 1 was provided
1053 | make_shared(size_t __n, constremove_extent_t<_Tp>& __u)
| ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1 error generated.
嗯,错误原因与gcc一样,下面我们看下make_shared的实现:
template<typename_Tp,typename..._Args>
inline shared_ptr<_NonArray<_Tp>>
make_shared(_Args&&... __args)
{
using_Alloc= allocator<void>;
_Alloc __a;
returnshared_ptr<_Tp>(_Sp_alloc_shared_tag<_Alloc>{__a},
std::forward<_Args>(__args)...);
}
也就是说在使用make_shared的时候,用到了std::forward
即完美转发。
结合我们前面的示例以及报错信息,可知:std::forward对某些情况下类型的推导会失败,进而导致我们常说的转发失败,所以为了解决前面的问题,我们需要显式指定类型:
std::shared_ptr<Y> ptr = std::make_shared<Y>(X{1, 2});
以上~~
如果对本文有异议或者有其他技术问题,可以加微信交流: