为什么编译器无法推导出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::remove_reference
定义中的std::forward
),以避免用户忘记指定类型。我不能得到的是:为什么它的实施方式可以防止扣除?为什么编译器不只是将forward
的模板参数推断为Arg
。
答案 0 :(得分:12)
std::forward
的声明如下:
template< class T >
T&& forward( typename std::remove_reference<T>::type& t );
typename std::remove_reference<T>::type
是非推断的上下文。编译器无法知道应推导出哪个T
,因为它不了解type
成员类型与给定T
之间的语义连接。它需要搜索所有类型以找到匹配并能够以某种方式消除冲突的歧义。这是不合理的,所以标准不允许这样做。
答案 1 :(得分:7)
您必须根据设计为forward
指定类型的原因是函数内a
会发生什么:
template<typename T,typename Arg>
T* factory( Arg&& a )
{
// 'a' is always an lvalue here
由于a
总是左值,因此a
本身没有足够的信息来确定它是作为左值还是右值传入的。 该信息仅可通过Arg
类型获得,该类型可以是X
或X&
。如果没有这些额外的类型信息,就不可能知道现在a
是否必须作为左值或右值转发......这就是你需要提供它的原因:
return new T(std::forward<Arg>(a));
}
答案 2 :(得分:0)
来自C ++ 11标准:
14.8.2.5从类型
中推导出模板参数未推断的上下文是:
- 使用qualified-id
指定的类型的嵌套名称说明符- decltype-specifier的表达式。
- 非类型模板参数或绑定的数组 子表达式引用模板参数。
- 函数的参数类型中使用的模板参数 具有在调用中使用的默认参数的参数 正在进行论证演绎。
等...
std::forward
声明如下:
template<typename _Tp>
constexpr _Tp&& forward(typename std::remove_reference<_Tp>::type& __t) noexcept
根据上面的第一句话:
typename std::remove_reference<_Tp>::type
是非推断的上下文。