【问题标题】:Why is std::remove_reference used in std::move?为什么在 std::move 中使用 std::remove_reference?
【发布时间】:2020-12-23 21:00:12
【问题描述】:

我尝试实现std::move,它使用std::remove_reference,但是没有它似乎也可以工作。请给我一个我的实现失败的例子,其中std::remove_reference 是必要的。

template <class type> type && move(type & source) { return (type &&) source; }
template <class type> type && move(type && source) { return (type &&) source; }

std::remove_reference 是否仅用于避免std::move 过载?

这是一个可以帮助你的测试类:

class test {
public :
    test() { }
    test(const test & source) { std::cout << "copy.\n"; }
    test(test && source) { std::cout << "move.\n"; }
};

不是How does std::move() transfer values into RValues? 的重复,因为我的问题包括一个似乎表明std::remove_reference 在这种情况下无用的示例 + 子问题。

【问题讨论】:

  • 这对您的问题没有影响,但您可能希望使用 static_cast 而不是 c 样式转换(因为这可能会退回到 reinterpret_cast)。
  • @SimonKraemer 它没有回答我的问题,我要求一个我的实现失败的例子,还有另一个问题“std::remove_reference 是否仅用于避免重载 std::移动 ?”。正如我在底部所说的那样。
  • @linternetsansfil 为什么会失败?它可能只是一种替代实现。但不符合标准。
  • @SimonKraemer 同样,回答问题而不是删除它会更快地解决每个人的问题。
  • @DanielLangr 我不知道为什么会失败,STL使用std::remove_reference所以应该是有用的。

标签: c++ syntax reference move-semantics


【解决方案1】:

我尝试实现 std::move,它使用 std::remove_reference,但是没有它似乎也可以工作。

是的,它正在工作,因为您明确提供了左值引用的重载。而std::remove_reference 仅在您使用转发引用时才相关。

如果你去掉这条线: Godbolt

template <class type> type && move(type & source) { return (type &&) source; }

并将您的函数称为:

test t2 = move(t1); //prints copy

要完成这项工作,您必须使用std::remove_referenceTry on Godbolt:

template <class type>
std::remove_reference_t<type> && move(type && source)
{
    return
    static_cast<std::remove_reference_t<type>&& >(source);
}

【讨论】:

  • 所以 remove_reference 实际上是没有必要的,使用重载是可以的,这只是一种替代方式,你的意思是什么?
  • 在你的情况下是没有必要的,因为你自己提供了更好的匹配(左值引用重载)。是的,它只是一种替代方案,但是它会增加很多混乱,所以我会避免将转发引用函数与简单的引用重载混合在一起:)
  • remove_reference 还有其他用例。例如:如果有两个参数具有不同类型的引用,那么您将需要remove_reference 才能使其工作
  • 我并不打算实际使用重载,我想知道哪种方式更好,似乎 remove_reference 是最好的。
  • 围绕这个主题有很多复杂性,所以是的,最好尝试和学习。如果你有兴趣,这里有几条相关的推文:twitter.com/NicoJosuttis/status/1300850885845753856?s=19 有一整本关于这个主题的书出来了 xD
【解决方案2】:

实现似乎有效,但两个函数声明重叠。

 template <class type> type && move(type && source) { return (type &&) source; }

这里type &amp;&amp; source 被解释为通用引用而不是右值引用。因此,它可以接受任何输入,包括左值引用,对于左值引用输入,它将返回一个左值引用输出 - 这是一个潜在的问题。

最好避免多个模板函数声明可以接受相同输入的情况,因为这会导致各种问题。虽然,也许有一个 C++ 标准规则,在处理通用引用时强制调用某些模板函数声明。您需要向语言律师咨询该信息。

您可以使用std::remove_reference 使用单个模板函数声明来实现移动,如下所示:

  template <class type>
  std::remove_reference_t<type> && move(type && source) 
  { 
      return (std::remove_reference_t<type>&&) source; 
  }

一般来说,std::remove_reference 在处理通用引用时有助于确定作为输入的类型并从中获取更多信息(尽管通常使用std::remove_cv_ref_t 或等效项)。

【讨论】:

  • 好的,我不知道它是特定于我的编译器 (g++) 的,但是对于两个重载,它会调用正确的。
  • 为什么还要去掉 const 和 volatile ?
  • 它是 remove_cvref_t。
  • @linternetansfil 当您尝试获取子类型时,例如,std::vector&lt;type&gt;::value_type。如果有const/reference/volatile,它会干扰使类型无法访问的过程,因为没有(const std::vector&lt;type&gt;&amp;)::value_type 这样的东西。
  • @linternetansfil remove_cvref_t 是 C++20 的东西......没能使用它。不得不自己写。
猜你喜欢
  • 2014-02-16
  • 2020-09-15
  • 1970-01-01
  • 2012-07-28
  • 1970-01-01
  • 2016-12-05
  • 2017-06-11
  • 2019-07-09
相关资源
最近更新 更多