【问题标题】:std::move a variable that is going to be overwrittenstd::move 一个将被覆盖的变量
【发布时间】:2020-07-30 05:34:52
【问题描述】:

我遇到了一些code

template<class InputIt, class T>
constexpr // since C++20
T accumulate(InputIt first, InputIt last, T init)
{
    for (; first != last; ++first) {
        init = std::move(init) + *first; // std::move since C++20
    }
    return init;
}

我有一个问题。为什么即使initint,我们也必须在init 上使用std::move?

【问题讨论】:

  • 对于任何类型,您都不必“必须”。
  • 重点在于你不知道T会是什么的通用代码。可以是int,也可以是MyBigFatObject
  • 我在这里看到的唯一优势是,如果有 T operator+(T&amp;&amp;, const T&amp;),这将是令人惊讶的。但我看到有一条注释指出std::move 是在 C++20 中添加的,因此可能即将对语言进行更改,这将使这种情况更加普遍。我很好奇为什么不直接使用init += *first
  • @FrançoisAndrieux:如果init 是一个具有足够大缓冲区的字符串,则会有很大的优势,因为这会从N 分配/副本变为0。此外,std::move 是在C 中添加的++11.
  • @MooingDuck std::move 在 C++20 中被添加到 该函数中。在链接的示例中已注明。使用init += *first 也会有同样的好处。虽然我看到std::string 确实存在带有右值引用的operator+,但我并不知道。

标签: c++ stdmove


【解决方案1】:

你是对的,移动 int 与复制它没有什么不同。

这里,std::move 仅在 T 的重载 operator+ 对左值和右值的行为不同时才会有用。

我从未听说过这样的类,但我想它可能对以巧妙的方式重载 + 的动态数组有用:

struct Vec
{
    std::vector<int> elems;
};

// Returns a completely new vector.
Vec operator+(const Vec &a, const Vec &b)
{
    assert(a.size() == b.size());
    Vec ret(a.size());
    for (std::size_t i = 0; i < a.size(); i++)
        ret.elems[i] = a.elems[i] + b.elems[i];
    return ret;
}
// Reuses storage of `a`.
Vec operator+(Vec &&a, const Vec &b)
{
    assert(a.size() == b.size());
    for (std::size_t i = 0; i < a.size(); i++)
        a.elems[i] += b.elems[i];
    return std::move(a);
}
// Reuses storage of `b`.
Vec operator+(const Vec &a, Vec &&b)
{
    return std::move(b) + a;
}
// Reuses storage of `a`.
Vec operator+(Vec &&a, Vec &&b)
{
    return std::move(a) + b;
}

编辑: 显然std::string 做了类似的事情:如果可能,它的+ 重复使用其中一个操作数的存储。 (感谢@FrançoisAndrieux 和@Artyer。)

【讨论】:

【解决方案2】:

Tint 的情况下,本身没有“需要”使用std::move。但是,由于我们可以将std::move 认为基本上意味着“请将此表达式转换为右值”,因此在Tint 的情况下编译此代码时,一个好的编译器不应引入任何开销。因此,从这个意义上说,这段代码肯定会提高某些类型的性能T,并且不太可能会损害移动是无操作的类型的性能。

【讨论】:

    【解决方案3】:

    我们没有“拥有”std::move init。我们(或者更确切地说,本例中的标准库)这样做是因为对于某些类型,移动比复制更有效。

    即使init是int?

    init 不一定是int。是T,是一个模板类型参数。

    【讨论】:

    • std::move 仅在您拥有我从未遇到过的T operator+(T&amp;&amp;, const T&amp;) 时才有帮助。标准库中是否有类似的东西,还是有其他我看不到的原因?否则,虽然它没有错,但它似乎不太可能是相关的。对我来说,使用init += *first 似乎比init = std::move(init) + *first; 更有可能做正确的事。
    • 但它到底是怎么回事?你能解释一下吗?
    • @FrançoisAndrieux:std::string 过载。 en.cppreference.com/w/cpp/string/basic_string/operator%2B
    • @MooingDuck 谢谢你的例子。
    • @Artyer 回想起来,这似乎很明显。谢谢你的澄清。
    猜你喜欢
    • 2012-08-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-11-30
    • 1970-01-01
    • 2014-02-16
    相关资源
    最近更新 更多