了解 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 获得,即 XX&。没有额外的类型信息,就不可能知道 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 是非推导上下文。