【问题标题】:To forward or move or not; how to determine which is preferred within the context of a class's usage?前进或移动或不前进;如何在类的使用情况下确定哪个是首选?
【发布时间】:2020-04-20 00:22:18
【问题描述】:
class Test {
public:
    typedef std::set<std::pair<double, double>> DataSet;

    explicit Test(const DataSet&& d) {
        for (auto &itr : d) {
            std::cout << "value1 = " << itr.first << '\n';
            std::cout << "value2 = " << itr.second << '\n';
        }
    }

};

int main() {
    //using namespace util;
    try {
        // Forwarding
        Test obj( std::forward<Test::DataSet>(
            { 
              { 10.0, 20.0 },
              { 30.0, 40.0 },
              { 50.0, 60.0 }
            }
        ));

        std::cout << '\n';

        // Move
        Test obj2(std::move(Test::DataSet(
            {
              { 10.0, 20.0 },
              { 30.0, 40.0 },
              { 50.0, 60.0 }
            }
           )
        ));

    } catch (const std::exception& e) {
        std::cerr << e.what() << std::endl;
        return EXIT_FAILURE;
    }

    return EXIT_SUCCESS;
}

【问题讨论】:

  • 也不行,直接传递初始化列表 - godbolt.org/z/AZW5xu。另外const X &amp;&amp; 通常是错误的,您需要X&amp;&amp;const X&amp;,具体取决于用例。
  • 霍尔特;我知道我可以通过初始化列表传递对;但是在两者之间,哪一个更好,为什么?至于const x&amp;&amp;const x&amp;,我并不特别关心它们...我这样编写构造函数只是为了能够编译std::movestd::forward...更多的是关于性能,两种不同算法之间的可读性和美观性。
  • “介于两者之间,哪个更好,为什么?” - 正如我所说,两者都不是,最好的版本是没有它们的版本。在这种情况下使用std::movestd::forward 没有意义,因此比较它们没有意义......

标签: c++ c++17 std move-semantics perfect-forwarding


【解决方案1】:

都没有。

Test::DataSet(/*...*/) 已经是一个可以绑定到右值引用的右值。 std::move 唯一要做的就是将左值转换为 xvalue(即右值),以便它可以绑定到右值引用,而左值不能。所以std::move 是多余的。

std::forward 仅根据其模板参数执行相同的条件。所以它也是多余的(充其量)。

const DataSet&amp;&amp; 作为参数没有意义。您需要DataSet&amp;&amp;,它只绑定到右值,或者const DataSet&amp;,它绑定到右值和左值。

如果您想利用传递的引用可能是右值这一事实(即通过从其成员中移动),那么您需要对函数进行两次重载。一个采用DataSet&amp;&amp;,它使用右值参数调用,另一种采用const DataSet&amp;,它使用左值参数调用。

在您的问题中 Test 函数的特定情况下,我看不出从成员中移动或对 DataSet 状态进行任何其他修改会有什么好处,所以我会选择只需将const DataSet&amp; 作为参数,并且永远不要专门为该函数使用std::moveing 或std::forwarding。

【讨论】:

  • user holt 在评论中给了我基本相同的答案;但是,我喜欢您解释在这种特殊情况下如何以及为什么它们不存在的方式。
  • 我不经常使用它们;所以有时为了清楚起见,我会问这样的问题。
【解决方案2】:

[针对 cme​​ts 编辑的代码示例]
来自 ISO 核心指南的有用的 advice。这表明您应该更喜欢 (const T&),但如果您真的需要速度,请特别添加 (T&&)。这样您就可以将小集合作为(L-Value)传递

const auto data = Test::DataSet{ 
          { 10.0, 20.0 },
          { 30.0, 40.0 },
          { 50.0, 60.0 }};
Test obj( data );

或(R值)

Test obj( Test::DataSet{ 
          { 10.0, 20.0 },
          { 30.0, 40.0 },
          { 50.0, 60.0 },
        });

添加额外的移动使第二种调用更有效,但建议仅在需要时使用这种类型的优化。您应该避免一个构造函数的语义与另一个构造函数不同,避免这种情况的最简单方法是只有一个构造函数。

就个人而言,即使经过 20 多年的 C++ 编程,我发现链接中的表格非常有用,我将其打印出来并贴在显示器上。

【讨论】:

  • 我真的很感激这个小费。是的,在大多数情况下T&amp; 就足够了,但有时我可能希望使用T&amp;&amp; 来提高性能。这取决于自定义数据集 - 我正在传递或移动的结构。通过 OpenGL - Vulkan 处理 3D 图形引擎的内部结构。因此,我正在尝试在引擎中使用的想法,以尝试使其具有最佳性能等通用性......
  • 我刚刚预订了 Stroustrup 和 Sutter 标记的 GitHub 页面!如果这两个保持它;你知道它应该是好的!我已经用 C++ 自学了差不多这么长时间了……我还在学习。我可以从头开始编写一个 3D 图形引擎,但是当深入研究 STL 时……我自己学到了很多东西,但我还没有学到很多东西。我已经知道moveforward;我在这里和那里都使用过它们,但是当您不经常使用它们时,更多的是要记住哪个用于哪个目的以及性能上的差异。
  • 这个链接对完美转发有很好的描述(当你应该使用 std::forward 时)eli.thegreenplace.net/2014/…
  • @FrancisCugler 如果您想了解moveforward,您可能应该查看专门的在线资源,而不是尝试通过反复试验来学习。例如,moveforward 之间不存在“性能”竞争,因为两者都有不同(但相关)的目的。此外,std::forward(几乎?)在模板代码之外没有用处。
  • 另外,如果您没有使用分析器分析您的编译器优化代码并验证它实际上是一个瓶颈。
猜你喜欢
  • 1970-01-01
  • 2014-07-22
  • 2012-11-15
  • 2014-07-04
  • 1970-01-01
  • 2012-02-24
  • 1970-01-01
  • 2020-06-04
  • 2010-10-03
相关资源
最近更新 更多