【问题标题】:How do two overloaded std::forward work? [duplicate]两个重载的 std::forward 是如何工作的? [复制]
【发布时间】:2023-11-06 13:04:01
【问题描述】:

我在 std 库中找到了 std::forward 的以下实现:

// TEMPLATE FUNCTION forward
template<class _Ty> inline
constexpr _Ty&& forward(
    typename remove_reference<_Ty>::type& _Arg) _NOEXCEPT
{   // forward an lvalue as either an lvalue or an rvalue
return (static_cast<_Ty&&>(_Arg));
}

template<class _Ty> inline
constexpr _Ty&& forward(
    typename remove_reference<_Ty>::type&& _Arg) _NOEXCEPT
{   // forward an rvalue as an rvalue
static_assert(!is_lvalue_reference<_Ty>::value, "bad forward call");
return (static_cast<_Ty&&>(_Arg));
}

第一个函数显然有效,但第二个我找不到有用的例子。 如果我尝试做这样的事情:

template<class _Ty> inline
constexpr _Ty&& my_forward(
   typename std::remove_reference<_Ty>::type&& _Arg) _NOEXCEPT
{   // forward an rvalue as an rvalue
   static_assert(!std::is_lvalue_reference<_Ty>::value, "bad forward call");
   return (static_cast<_Ty&&>(_Arg));
}

template<typename T>
T getRValue()
{
    return std::remove_reference<T>::type{};
}

template<typename T>
void setValue(T && i)
{
   my_forward<T>(getRValue<T>());
}

int main()
{
    int i = 1;
    setValue(i);
}

检查第二个函数何时被调用,我得到了错误:

Error   C2664   '_Ty my_forward<T>(int &&) noexcept': cannot convert argument 1 from 'int' to 'int &&'

有谁知道在哪种情况下调用第二个重载函数(用我的话来说是 my_forward)? 或者可能是一些很好的例子? 顺便说一句,我的编译器是 MSVC 2015

谢谢大家帮忙!!

【问题讨论】:

  • 评论说“将右值作为右值转发”,但您将 rvalue 左值作为左值转发(并且还有未定义行为的情况)。这不是编写函数的目的。
  • @Johannes Schaub:为什么? getRValue() 提供prvalue,它可以作为my_forward的参数。你能举一个有用的例子,在我的例子中使用 my_forward 作为右值吗?
  • @DenisKotov: "getRValue() 提供prvalue" 是吗?它返回一个T,但T 可以是一个引用。因此,它可能是一个左值。
  • @Johannes Schaub:抱歉不正确。它是xvalue,而不是lvalue,因为函数的返回值可以被重用,但它还没有名字
  • 这个也很相关——*.com/q/29135698/241631

标签: c++ c++11 forward-declaration


【解决方案1】:
int bad_forward_call() {
  return std::forward<int&>(7);
}

这将调用该版本。

请注意,我将右值作为左值转发。不允许。

如果T 是引用类型,您的getRValue 将无法获得右值。

【讨论】:

  • 不,这个版本不叫那个版本。由于静态断言,它甚至无法编译
  • @DenisKotov 在那个版本中有一个static_assert...我相信我正在打。
【解决方案2】:

我发现了调用这个函数的案例:

my_forward<int&>(8); // This calls forward(typename remove_reference<_Ty>::type&& _Arg)
But it won't compile 'cause we try convert rvalue to lvalue

my_forward<int&&>(8); // This calls forward(typename remove_reference<_Ty>::type&& _Arg)
But it compiles successfully

如果它可能对某人有用,std::forward 的目的是将操作转发到另一个类或函数。

考虑以下示例:

template<typename T>
void precompute(T & t)
{
   std::cout << "lvalue reference";
}

template<typename T, 
         std::enable_if_t<!std::is_reference<T>::value, bool> = true>
void precompute(T && t)
{
   std::cout << "rvalue reference";
}

template<typename T>
void calculate(T && t)
{
   precompute(t); // Every time called precompute(T & t)
}

或者像这样:

template<typename T>
void precompute(T & t)
{
   std::cout << "lvalue reference";
}

template<typename T, 
         std::enable_if_t<!std::is_reference<T>::value, bool> = true>
void precompute(T && t)
{
   std::cout << "rvalue reference";
}

template<typename T>
void calculate(T && t)
{
   precompute(std::move(t)); // Every time called precompute(T && t)
}

如您所见,我们遇到了问题!两个例子都不让我们满意。

在第一个示例中,每次都会调用 lvalue 函数,并且没有什么可以先用 rvalue 调用。

在第二个例子中,每次都会调用 rvalue 函数,并且没有什么可以先用 lvalue 调用。

解决方案是将决策转发给被调用函数:

template<typename T>
void precompute(T & t)
{
   std::cout << "lvalue reference";
}

template<typename T, 
         std::enable_if_t<!std::is_reference<T>::value, bool> = true>
void precompute(T && t)
{
   std::cout << "rvalue reference";
}

template<typename T>
void calculate(T && t)
{
   precompute(std::forward<T>(t)); // Will be called either precompute(T & t) or precompute(T && t) depends on type of t
}

在这种情况下,我们必须确保将调用适当的版本。 这就是为什么我们转发(意味着:“请检查 t 的类型,如果它是 rvalue -> 调用 rvalue 版本的函数,如果它是 lvalue -> 调用 lvalue 版本的函数")这个操作来调用函数。

【讨论】:

    最近更新 更多