Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/164.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181

Warning: file_get_contents(/data/phpspider/zhask/data//catemap/3/templates/2.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ std::forward如何接收正确的参数?_C++_Templates_C++11_Rvalue Reference_Perfect Forwarding - Fatal编程技术网

C++ std::forward如何接收正确的参数?

C++ std::forward如何接收正确的参数?,c++,templates,c++11,rvalue-reference,perfect-forwarding,C++,Templates,C++11,Rvalue Reference,Perfect Forwarding,考虑: void g(int&); 无效g(int&&); 模板 无效f(T&x) { g(std::forward(x)); } int main() { f(10); } 既然id表达式x是一个左值,并且std::forward具有左值和右值的重载,为什么调用不绑定到接受左值的std::forward的重载 模板 constexpr T&forward(std::remove_reference_T&T)无例外; 为什么调用不绑定到采用左值的std::forward重载 它确实是这样做的

考虑:

void g(int&);
无效g(int&&);
模板
无效f(T&x)
{
g(std::forward(x));
}
int main()
{
f(10);
}
既然id表达式
x
是一个左值,并且
std::forward
具有左值和右值的重载,为什么调用不绑定到接受左值的
std::forward
的重载

模板
constexpr T&forward(std::remove_reference_T&T)无例外;
为什么调用不绑定到采用左值的
std::forward
重载

它确实是这样做的,但是
std::forward
它的模板参数,你告诉它它是什么类型的,这就是神奇之处。您正在向
f()
传递一个pr值,因此
f()
推断
[T=int]
。然后它调用
forward
的左值重载,由于
forward
中发生的返回类型和
静态转换
将属于
int&
类型,因此调用
void g(int&&)
重载

如果要将左值传递给
f()

f()
推断出
[T=int&]
,再次调用
向前
的相同左值重载,但这次返回类型和
静态转换
都是
int&
,同样是因为引用折叠规则。这将调用
void g(int&)
重载


Howard已经为您的问题提供了一个很好的答案,关于为什么需要
forward
的右值重载,但我将添加一个人为的示例,展示两个版本的实际情况

这两个重载背后的基本思想是,当传递给
forward
的表达式的结果产生右值(prvalue或xvalue)时,将调用右值重载

假设您有一个类型
foo
,它有一对ref限定的
get()
成员函数重载。带有
&
限定符的一个返回
int
,而另一个返回
int&

struct foo
{
    int i = 42;
    int  get() && { return i; }
    int& get() &  { return i; }
};
然后说
f()
对传递给它的任何对象调用
get()
成员函数,并将其转发给
g()

模板
自动f(T&T)
{
std::cout它确实绑定到
std::forward
的重载,以获取左值:

template <class T>
constexpr T&& forward(remove_reference_t<T>& t) noexcept;
因为
f
中的
T
推导为
int
。因此此重载将左值
int
强制转换为xvalue,具体如下:

static_cast<int&&>(t)
抓住左值

过载2:

template <class T> constexpr T&& forward(remove_reference_t<T>&& t) noexcept;
template constepr T&&forward(删除引用)无例外;
捕获右值(即xvalues和prvalues)

重载1可以将其左值参数强制转换为左值或xvalue(为了重载解析的目的,后者将被解释为右值)

重载2只能将其右值参数强制转换为xvalue(出于重载解析的目的,它将被解释为右值)

重载2适用于中标记为“B.应将右值转发为右值”的情况。简而言之,此情况启用:

std::forward<T>(u.get());
std::forward(u.get());
如果不确定
u.get()
是否返回左值或右值,但是如果
T
不是左值引用类型,则希望移动返回的值。但不使用
std::move
,因为如果
T
是左值引用类型,则不希望从返回中移动

我知道这听起来有点做作。但是,设置激发动机的用例,说明
std::forward
应该如何处理显式提供的模板参数和普通参数的隐式提供的表达式类别的所有组合,却遇到了很大的麻烦

这本书不容易阅读,但将模板和普通参数组合成
std::forward
的基本原理仍在讨论中。当时,这在委员会上是有争议的,并不容易推销

std::forward
的最终形式并不完全符合建议。但是它确实通过了中介绍的所有六项测试。

std::forward如何接收正确的参数? 为了实现完美的转发,如您的代码所示:

template<class T>
void f(T&& x)
{
    g(std::forward<T>(x));
}
  • 如果类型的左值的传递函数为
    string
    string&
    ,则 转发函数
    \u Tp
    将是
    字符串&
    \uu t
    将是
    字符串&
    等于
    字符串&
    字符串&&
    等于
    字符串&
    (引用折叠)
  • 如果类型的传递函数右值为
    string
    string&
    ,则 转发函数
    \u Tp
    将是
    字符串
    \u t
    将是
    字符串&
    将是
    字符串&
函数的作用是什么?
  • 如果rvalue到rvalue(重载2)或lvalue到lvalue(重载1),则无需执行任何操作,只需返回
  • 如果您不小心或其他原因,请将rvalue引导到lvalue,通过
    static\u assert
    (重载2)给您一个编译错误
  • 如果将左值转换为右值(重载1),则与std::move相同,但不方便
正如Scott Meyers所说:

假设std::move和std::forward都归结为强制转换,那么 唯一的区别是std::move总是强制转换,而 std::forward有时会这样做,您可能会问我们是否可以 不要使用std::move,只要在任何地方使用std::forward即可。从 纯粹从技术角度来看,答案是肯定的:std::forward可以 这一切。std::move不是必需的。当然,这两个函数都不是必需的 真的很有必要,因为我们可以到处写演员,但我 希望我们同意这会很恶心


右值重载的作用是什么? 我不确定,它应该用在你真正需要的地方。比如Howard Hinnant说:

过载2用于外壳标签
template <class T>
constexpr T&& forward(remove_reference_t<T>& t) noexcept;
template <class T> constexpr T&& forward(remove_reference_t<T>&& t) noexcept;
std::forward<T>(u.get());
template<class T>
void f(T&& x)
{
    g(std::forward<T>(x));
}
  template<typename _Tp>
    constexpr _Tp&&
    forward(typename std::remove_reference<_Tp>::type& __t) noexcept
    { return static_cast<_Tp&&>(__t); } //overload 1


  template<typename _Tp>
    constexpr _Tp&&
    forward(typename std::remove_reference<_Tp>::type&& __t) noexcept
    {
      static_assert(!std::is_lvalue_reference<_Tp>::value, "template argument"
            " substituting _Tp is an lvalue reference type");
      return static_cast<_Tp&&>(__t);
    }// overload 2