了解std :: forward

时间:2015-08-25 13:08:54

标签: c++ c++11 perfect-forwarding

为什么编译器无法推导出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

3 个答案:

答案 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类型获得,该类型可以是XX&。如果没有这些额外的类型信息,就不可能知道现在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是非推断的上下文。