标签: move-semantics

为什么C++ 11具有值参数的隐式移动,而不是rvalue参数?

在C++ 11中,值参数(和其他值)在返回时享受隐式移动:

A func(A a) {
    return a; // uses A::A(A&&) if it exists
}
Run Code Online (Sandbox Code Playgroud)

至少在MSVC 2010中,右值参考参数需要std::move:

A func(A && a) {
    return a; // uses A::A(A const&) even if A::A(A&&) exists
}
Run Code Online (Sandbox Code Playgroud)

我认为内部函数,右值引用和值的行为类似,唯一的区别是在值的情况下,函数本身负责销毁,而对于右值引用,责任在外面.

在标准中对待它们的动机是什么?

c++ rvalue-reference move-semantics c++11 c++20

21
推荐指数
3
解决办法
3688
查看次数

为什么复制和移动构造函数一起调用?

请考虑以下代码:

#include <iostream>
#include <vector>
using namespace std;

class A
{
public:
     A(int) { cout << "int" << endl; }
     A(A&&) { cout << "move" << endl; }
     A(const A&) { cout << "copy" << endl; }
};

int main()
{
    vector<A> v
    {
        A(10), A(20), A(30)
    };

    _getch();
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

输出是:

int
int
int
copy
copy
copy
Run Code Online (Sandbox Code Playgroud)

A(10),A(20)并且A(30)是临时对象,对不对?

那么为什么复制构造函数被调用?不应该调用移动构造函数吗?

路过move(A(10)),move(A(20)),move(A(30))相反,输出为:

int
move
int
move
int
move
copy …
Run Code Online (Sandbox Code Playgroud)

c++ copy-constructor move-constructor move-semantics c++11

21
推荐指数
2
解决办法
1569
查看次数

使用std :: pair或std :: tuple的移动语义

假设你想利用移动语义,但你的一个可移动类需要成为一部分std::pair.目的是创建一个函数,该函数返回一个std::pair可被视为右值并转发的函数.

但我无法看到如何做到这一点,除非对其std::pair自身进行内部更改,以使其了解移动语义.

请考虑以下代码:

struct Foo
{
 Foo() { }

 Foo(Foo&& f) { }

 private:

 Foo(const Foo& f) { } // do not allow copying
};

int main() 
{
 Foo f;
 std::pair<Foo, int> res = std::make_pair(f, 10); // fails due to private copy constructor
}
Run Code Online (Sandbox Code Playgroud)

问题是std::make_pair,除了std::pair构造函数本身之外,还需要两个对象并尝试制作它们的内部副本.这导致它尝试并调用复制构造函数.但在我的例子中,我希望能够新对移动res,并确保不会制作副本.我认为除非std::pair自己在内部定义了以下构造函数,否则这是不可能的:

pair(T1&& t1, T2&& t2) : first(std::move(t1)), second(std::move(t2))
Run Code Online (Sandbox Code Playgroud)

但它没有,至少在我使用的编译器上没有(gcc 4.3.2).这可能是因为我的编译器是简单地外的日期,而事实上新版本拥有这一举动感知构造.但是我对移动语义的理解目前有点不稳定,所以我不确定我是否只是在这里忽略了一些东西.那么,我是否正在努力实现,而不是实际重新实现std::pair?或者我的编译器是否已过时?

c++ move-constructor move-semantics c++11 std-pair

20
推荐指数
1
解决办法
1万
查看次数

std :: array应该有移动构造函数吗?

移动无法在std :: array上高效实现(O(1)),为什么它有移动构造函数?

c++ move-semantics c++11

20
推荐指数
2
解决办法
3306
查看次数

std :: vector :: emplace_back和std :: move

使用std::vector::emplace_backstd::move一起有什么好处?或者它只是多余的,因为std::vector::emplace_back会做一个inplace-construction?

案例澄清:

std::vector<std::string> bar;
Run Code Online (Sandbox Code Playgroud)

第一:

bar.emplace_back(std::move(std::string("some_string")));
Run Code Online (Sandbox Code Playgroud)

第二:

std::string str("some_string");
bar.emplace_back(std::move(str));
Run Code Online (Sandbox Code Playgroud)

第三:

bar.emplace_back(std::move("some_string"));
Run Code Online (Sandbox Code Playgroud)

c++ vector move-semantics c++11

20
推荐指数
3
解决办法
9667
查看次数

移动字符串流的.str()成员是否合法?

请考虑以下示例:

#include <sstream>
template <typename T>
inline std::string to_string(T const & op) {
    std::ostringstream result;
    result << op;
    return result.str();
}
Run Code Online (Sandbox Code Playgroud)

如果我要返回结果,而不是result.str()它将自动成为右值.不是结果中包含的字符串(我假设).我的期望是它被复制并且副本作为右值返回.

所以我的问题是,合法的是:

return std::move(result.str());
Run Code Online (Sandbox Code Playgroud)

我认为它是,期望流留下一个有效的空字符串.但我不确定实际做到这一点.

c++ rvalue move-semantics c++11

20
推荐指数
1
解决办法
1225
查看次数

我应该删除移动构造函数和智能指针的移动分配吗?

我正在实现一个简单的智能指针,它基本上跟踪它处理的指针的引用数量.

我知道我可以实现移动语义,但我不认为复制智能指针非常便宜.特别是考虑到它引入了产生令人讨厌的错误的机会.

这是我的C++ 11代码(我省略了一些不必要的代码).欢迎提出一般性意见.

#ifndef SMART_PTR_H_
#define SMART_PTR_H_

#include <cstdint>

template<typename T>
class SmartPtr {
private:
    struct Ptr {
        T* p_;
        uint64_t count_;
        Ptr(T* p) : p_{p}, count_{1} {}
        ~Ptr() { delete p_; }
    };
public:
    SmartPtr(T* p) : ptr_{new Ptr{p}} {}
    ~SmartPtr();

    SmartPtr(const SmartPtr<T>& rhs);
    SmartPtr(SmartPtr<T>&& rhs) =delete;

    SmartPtr<T>& operator=(const SmartPtr<T>& rhs);
    SmartPtr<T>& operator=(SmartPtr<T>&& rhs) =delete;

    T& operator*() { return *ptr_->p_; }
    T* operator->() { return ptr_->p_; }

    uint64_t Count() const { return ptr_->count_; }

    const T* …
Run Code Online (Sandbox Code Playgroud)

c++ smart-pointers move-semantics c++11

20
推荐指数
1
解决办法
3006
查看次数

默认移动构造函数/赋值和已删除的复制构造函数/赋值

根据标准,

如果类X的定义没有显式地声明一个移动构造函数,那么当且仅当一个移动构造函数被隐式声明为默认值时

- X没有用户声明的复制构造函数,

- X没有用户声明的复制赋值运算符,

- X没有用户声明的移动赋值运算符,和

- X没有用户声明的析构函数.

现在以下无法编译

# include <utility>

class Foo
{
public:
  Foo() = default;
  Foo(Foo const &) = delete;
};

int main()
{
  Foo f;
  Foo g(std::move(f)); // compilation fails here
  return 0;
}
Run Code Online (Sandbox Code Playgroud)

所以似乎删除的函数被认为是用户定义的,这是有道理的(它不是它的默认实现).但是,在那种特殊情况下,如何删除复制construtor/assignment mess默认移动构造函数/赋值?

我认为这个问题具有实际意义,因为手动生成和特别是 这种默认函数的维护是容易出错的,同时,诸如std::unique_ptr类成员之类的类的使用的(正当的)增加使得不可复制的类比以前更常见.

c++ move-semantics deleted-functions

20
推荐指数
1
解决办法
1万
查看次数

const_cast和std :: move从非引用中删除constness

我有一个外部库,我无法修改.该库声明了一个模板函数,该函数由于某种原因返回const非引用对象:

template<class C>
const C foo();
Run Code Online (Sandbox Code Playgroud)

我有另一个外部库,我无法修改.该库声明了一个不可复制的类,并且只有非const对象的移动构造函数:

struct bar {
    bar();
    bar(const bar&)=delete;
    bar(bar&&);
};
Run Code Online (Sandbox Code Playgroud)

现在我需要使用foo<bar>.一个简单的用法:

bar buz() {
    return foo<bar>();
}
Run Code Online (Sandbox Code Playgroud)

失败了

main.cpp: In function 'bar buz()':
main.cpp:13:21: error: use of deleted function 'bar::bar(const bar&)'
     return foo<bar>();
                     ^
main.cpp:8:5: note: declared here
     bar(const bar&)=delete;
     ^~~
Run Code Online (Sandbox Code Playgroud)

这是有道理的,并没有简单的解决方法使代码编译.

但是,如果我添加一些更复杂的解决方法:

bar buz() {
    return const_cast<bar&&>(std::move(foo<bar>()));
}
Run Code Online (Sandbox Code Playgroud)

它编译并且整个代码按预期工作(不仅是上面的简化示例,而且我的真实代码也是如此).

但是,它是安全的,还是我遇到了一些未定义的行为?有没有更好的解决方法?


我已阅读并理解有关返回的问题const从功能(1,2),以及常见的答案似乎是返回const对象在现代C++气馁,但我的问题是不是这件事,但我如何能解决该情况当外部库返回const对象时.

c++ const move-semantics c++11

20
推荐指数
2
解决办法
867
查看次数

是否有可能std :: move本地堆栈变量?

请考虑以下代码:

struct MyStruct
{
    int iInteger;
    string strString;
};

void MyFunc(vector<MyStruct>& vecStructs)
{
    MyStruct NewStruct = { 8, "Hello" };
    vecStructs.push_back(std::move(NewStruct));
}

int main()
{
    vector<MyStruct> vecStructs;
    MyFunc(vecStructs);
}
Run Code Online (Sandbox Code Playgroud)

为什么这样做?

在调用MyFunc时,返回地址应放在当前线程的堆栈上.现在创建NewStruct对象,它也应该被放置在堆栈上.使用std :: move,我告诉编译器,我不打算再使用NewStruct引用了.他可以偷走记忆.(push_back函数是具有移动语义的函数.)

但是当函数返回并且NewStruct超出范围时.即使编译器不会从堆栈中删除最初存在的结构所占用的内存,他至少也要删除先前存储的返回地址.

这将导致碎片堆栈,未来的分配将覆盖"移动"的内存.

有人可以向我解释一下吗?


编辑:首先:非常感谢您的回答.但是从我学到的东西,我仍然无法理解,为什么以下不能像我期望的那样工作:

struct MyStruct
{
    int iInteger;
    string strString;
    string strString2;
};

void MyFunc(vector<MyStruct>& vecStructs)
{
    MyStruct oNewStruct = { 8, "Hello", "Definetly more than 16 characters" };
    vecStructs.push_back(std::move(oNewStruct));

    // At this point, oNewStruct.String2 should be "", because its memory was stolen.
    // But only …
Run Code Online (Sandbox Code Playgroud)

c++ move move-semantics c++11

20
推荐指数
2
解决办法
1万
查看次数