C++ std::移动将被覆盖的变量

C++ std::移动将被覆盖的变量,c++,stdmove,C++,Stdmove,我遇到了一些麻烦 模板 constexpr//自C++20以来 T累计(先输入,后输入,后输入) { for(;first!=last;++first){ init=std::move(init)+*first;//std::move自C++20 } 返回init; } 我有个问题。即使init是int,为什么我们必须使用std::move oninit?我们不必“必须”移动std::moveinit。我们(或者更确切地说,本例中的标准库)这样做是因为对于某些类型,移动比复制更有效 即使ini

我遇到了一些麻烦

模板
constexpr//自C++20以来
T累计(先输入,后输入,后输入)
{
for(;first!=last;++first){
init=std::move(init)+*first;//std::move自C++20
}
返回init;
}
我有个问题。即使
init
int
,为什么我们必须使用std::move on
init

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

即使init是int


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

T
int
的情况下,本质上不需要使用
std::move
。但是,由于我们可以将
std::move
看作基本意思是“请将此表达式转换为右值”,因此在
T
int
的情况下,一个好的编译器在编译此代码时不应该引入任何开销。因此,从这个意义上讲,这段代码肯定会提高某些类型的性能,而且它不太可能影响禁止移动的类型的性能。

没错,移动
int
与复制它没有什么不同

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

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

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::向量元素; }; //返回一个全新的向量。 向量运算符+(常量向量和a、常量向量和b) { 断言(a.size()==b.size()); Vec-ret(a.size()); 对于(std::size_t i=0;i
Edit:显然
std::string
做了类似的事情:它的
+
尽可能重用其中一个操作数的存储。(感谢@FrançoisAndrieux和@Artyer。)

对于任何类型,你都不必“必须”。关键是在通用代码中,你不知道什么是
t
。它可以是
int
,也可以是
MyBigFatObject
。我在这里看到的唯一优势是如果有
T操作符+(T&&,const T&)
,这将是令人惊讶的。但是我注意到,
std::move
是在C++20中添加的,因此可能会有一个即将到来的语言更改,使这种情况更加常见。我很好奇为什么
init+=*first
不会在intead中使用。@FrançoisAndrieux:如果
init
是一个缓冲区足够大的字符串,那么它有一个巨大的优势,因为它从
N
allocations/copies变为0。另外,在C++11中添加了
std::move
。@MooingDuck
std::move
被添加到C++20中的该函数中。这在链接的示例中有所说明。使用
init+=*first
也有同样的好处。虽然我看到带有右值引用的
操作符+
确实存在于
std::string
,我不知道。
std::move
只有在您有
t操作符+(t&&,const t&)
时才有用,我以前从未遇到过它。在标准库中是否有类似的内容,或者是否有其他我看不到的原因?否则,虽然这没有错,但似乎永远都不可能有关联。使用
init+=*first
似乎比
init=std::move(init)+*first更可能做正确的事情对我来说。但是引擎盖下的情况如何?你能解释一下吗?@FrançoisAndrieux:
std::string
有过载@感谢你的例子。@Artyer回想起来,这似乎是显而易见的。谢谢你的澄清。存在。一般来说,我假设它将被实现为
returnstd::move(lhs+=rhs)
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;
}