了解 std::forward
Understanding std::forward
为什么编译器无法推导出 std::forward
的模板参数?
我的意思是:
#include <memory>
#include <iostream>
struct X{};
struct A{
A( const X& ) { std::cout << "cpy ctor\n"; }
A( X&& ) { std::cout << "move ctor\n"; }
};
X foo() { return {}; }
template<typename T,typename Arg>
T* factory( Arg&& a )
{
return new T(std::forward(a));
// ----------^^^^^^^^^^^^^^^ error: can't deduce template parameter
}
int main()
{
factory<A>(foo());
}
我知道这是一个设计选择(由于 std::forward
定义中的 std::remove_reference
)以避免用户忘记指定类型。我无法得到的是:为什么它的实施方式可以防止扣除?为什么编译器不只是将 forward
的模板参数推导为 Arg
.
std::forward
声明如下:
template< class T >
T&& forward( typename std::remove_reference<T>::type& t );
typename std::remove_reference<T>::type
是 非推导上下文 。编译器无法知道应该推导哪个 T
,因为它不理解 type
成员类型和给定的 T
之间的语义联系。它需要搜索所有类型以找到匹配项并能够以某种方式消除冲突的歧义。这是不合理的,所以标准不允许。
您必须为 forward
指定类型的原因是函数内部 a
发生的情况:
template<typename T,typename Arg>
T* factory( Arg&& a )
{
// 'a' is always an lvalue here
因为a
总是一个左值,a
本身没有足够的信息来确定它是否被传递为左值或右值。 That 信息只能通过类型 Arg
获得,即 X
或 X&
。没有额外的类型信息,就不可能知道 a
是否必须作为左值或右值转发...这就是为什么您需要提供它:
return new T(std::forward<Arg>(a));
}
来自 C++11 标准:
14.8.2.5 Deducing template arguments from a type
The non-deduced contexts are:
— The nested-name-specifier of a type that was specified using a qualified-id
— The expression of a decltype-specifier.
— A non-type template argument or an array bound in which a
subexpression references a template parameter.
— A template parameter used in the parameter type of a function
parameter that has a default argument that is being used in the call
for which argument deduction is being done.
etc...
std::forward
声明如下:
template<typename _Tp>
constexpr _Tp&& forward(typename std::remove_reference<_Tp>::type& __t) noexcept
根据上面第一句:
typename std::remove_reference<_Tp>::type
是非推导上下文。
为什么编译器无法推导出 std::forward
的模板参数?
我的意思是:
#include <memory>
#include <iostream>
struct X{};
struct A{
A( const X& ) { std::cout << "cpy ctor\n"; }
A( X&& ) { std::cout << "move ctor\n"; }
};
X foo() { return {}; }
template<typename T,typename Arg>
T* factory( Arg&& a )
{
return new T(std::forward(a));
// ----------^^^^^^^^^^^^^^^ error: can't deduce template parameter
}
int main()
{
factory<A>(foo());
}
我知道这是一个设计选择(由于 std::forward
定义中的 std::remove_reference
)以避免用户忘记指定类型。我无法得到的是:为什么它的实施方式可以防止扣除?为什么编译器不只是将 forward
的模板参数推导为 Arg
.
std::forward
声明如下:
template< class T >
T&& forward( typename std::remove_reference<T>::type& t );
typename std::remove_reference<T>::type
是 非推导上下文 。编译器无法知道应该推导哪个 T
,因为它不理解 type
成员类型和给定的 T
之间的语义联系。它需要搜索所有类型以找到匹配项并能够以某种方式消除冲突的歧义。这是不合理的,所以标准不允许。
您必须为 forward
指定类型的原因是函数内部 a
发生的情况:
template<typename T,typename Arg>
T* factory( Arg&& a )
{
// 'a' is always an lvalue here
因为a
总是一个左值,a
本身没有足够的信息来确定它是否被传递为左值或右值。 That 信息只能通过类型 Arg
获得,即 X
或 X&
。没有额外的类型信息,就不可能知道 a
是否必须作为左值或右值转发...这就是为什么您需要提供它:
return new T(std::forward<Arg>(a));
}
来自 C++11 标准:
14.8.2.5 Deducing template arguments from a type
The non-deduced contexts are:
— The nested-name-specifier of a type that was specified using a qualified-id
— The expression of a decltype-specifier.
— A non-type template argument or an array bound in which a subexpression references a template parameter.
— A template parameter used in the parameter type of a function parameter that has a default argument that is being used in the call for which argument deduction is being done.
etc...
std::forward
声明如下:
template<typename _Tp>
constexpr _Tp&& forward(typename std::remove_reference<_Tp>::type& __t) noexcept
根据上面第一句:
typename std::remove_reference<_Tp>::type
是非推导上下文。