在移动构造函数之前,如果返回了一个temp,最好让它返回const以避免让某人分配给temp变量
现在似乎移动构造函数不处理const返回,似乎不返回const将是最佳实践
但是,现在你回到了合法分配临时变量的人的问题
快速示例(假设一个非常昂贵的复制构造器):
MyClass a=1;
MyClass b=2;
(a+b)=3; // how do I disallow this and allow move constuctors for `operator+`
Run Code Online (Sandbox Code Playgroud)
if operator+返回const MyClass然后它将无法编译,但任何常规用法将使用昂贵的复制构造函数而不是廉价的移动构造函数.
最近我一直在研究C++ 11中的移动语义.我印象非常深刻,以至于我迫不及待地试着把它弄脏了.以下是我的代码:
#include <iostream>
using namespace std;
class ArrayWrapper
{
public:
// default constructor produces a moderately sized array
ArrayWrapper ()
: _p_vals( new int[ 64 ] )
, _size( 64 )
{
cout << "Default constructor: " << this << endl;
}
explicit ArrayWrapper (int n)
: _p_vals( new int[ n ] )
, _size( n )
{
cout << "Constructor: " << this << endl;
}
// move constructor
ArrayWrapper (ArrayWrapper&& other)
: _p_vals( other._p_vals )
, _size( …Run Code Online (Sandbox Code Playgroud) 我知道的是,除非另有说明,所有的标准库函数接受右值引用参数都保证要离开移动,从参数中有效,但不确定状态,而这里的一些例子可以表现出不确定的行为结果,而是根本问题不依赖于此.
以下程序:
// testmove1.cpp
#include <iostream>
#include <string>
int main() {
std::string s1{"String"};
std::cout << "s1: [" << s1 << "]" << std::endl;
std::string s2{std::move(s1)};
std::cout << "s2: [" << s2 << "]" << std::endl;
std::cout << "s1 after move: [" << s1 << "]" << std::endl; // Undefined
return 0;
}
Run Code Online (Sandbox Code Playgroud)
输出:
paul@local:~$ ./testmove1
s1: [String]
s2: [String]
s1 after move: []
paul@local:~$
Run Code Online (Sandbox Code Playgroud)
s1移动后的输出对我来说似乎没有定义,但是留空的字符串至少是一个可行的选择.Valgrind报告为该程序进行了单一分配,这正是您所期望的.
如果我做了一些非常相似的事情,但是有了一个班级成员,我会得到不同的结果:
// testmove2.cpp
#include <iostream>
#include <string>
class MyClass {
std::string m_data;
public:
MyClass(const …Run Code Online (Sandbox Code Playgroud) 我有一些Foo结构需要知道其他对象的状态要初始化,所以我为它创建一个工厂方法:
struct Foo {
Foo(int x) : x_(x) {}
int x_;
};
struct FooFactory {
Foo MakeFoo() {
return Foo(++counter);
}
int counter = 0;
};
Run Code Online (Sandbox Code Playgroud)
因此,虽然呼叫者可以这么做Foo(++factory.counter),但能够说出来更清洁factory.MakeFoo().
但是这段代码需要复制,并说我们想避免这种情况.我们可以使用移动构造函数.
struct Foo {
Foo(int x) : x_(x) {}
Foo(Foo& foo) = delete;
Foo(Foo&& foo) : Foo(foo.x_) { foo.x_ = 0; }
int x_;
};
struct FooFactory {
Foo MakeFoo() {
return Foo(++counter);
}
int counter = 0;
};
Run Code Online (Sandbox Code Playgroud)
哪个有效,但看起来仍然比我想要的"更多".类似的东西Foo foo = factory.MakeFoo()仍然会创建一个临时的内部, …
struct foo{
int* i;
foo(): i(new int(42)){}
foo(const foo&) = delete;
foo(foo&&) = default;
~foo(){
std::cout << "destructor: i" << std::endl;
delete(i);
}
};
int main()
{
foo f;
auto sp_f = std::make_shared<foo>(std::move(f));
}
Run Code Online (Sandbox Code Playgroud)
这很糟糕,因为似乎析构函数f一旦进入就会被调用shared_ptr.在shared_ptr将已删除的指针,它超出范围,这意味着该指针将被删除两次之后将其删除.
我该如何避免这个问题?
最初我认为移动构造函数不会调用临时对象析构函数,但是当我尝试它时调用析构函数.所以当我们从移动构造函数中窃取数据时,我得到双删除错误.
#include <iostream>
using namespace std;
class A
{
public:
A()
: name("default")
{
cout<<"i am default\n";
data = new char[20];
}
A(A&& t)
: name("move")
{
data = t.data;
cout<<"i am move\n";
}
~A()
{
delete data;
cout<<"I am done:"<<name<<endl;
}
char * data;
string name;
};
A getA()
{
A obj;
return obj;
}
int main()
{
A test(std::move(getA()));
}
Run Code Online (Sandbox Code Playgroud) 有没有办法为类编写一个拷贝构造函数(比如说Copyable,它可以保存std::unique_ptr一个Base类(但实际上是存储Derived对象).
快速测试显示预期的切片发生,因为Copyable不知道它所持有的实际类型.所以我想一个clone方法是必要的,但我想知道是否有办法让编译器以更好的方式处理它?
切片代码:
#include <algorithm>
#include <iostream>
#include <memory>
struct Base
{
Base(int i = 0) : i(i) {}
virtual ~Base() = default;
int i;
virtual int f() { return i; }
};
struct Derived : Base
{
Derived() = default;
virtual int f() override { return 42; }
};
struct Copyable
{
Copyable(std::unique_ptr<Base>&& base) : data(std::move(base)) {}
Copyable(const Copyable& other)
{
data = std::make_unique<Base>(*other.data);
}
std::unique_ptr<Base> data; …Run Code Online (Sandbox Code Playgroud) smart-pointers deep-copy copy-constructor move-semantics c++11
标题几乎总结了我的问题.更详细:我知道当我在C++ 11中声明移动构造函数和移动赋值运算符时,我必须"使其他对象变量为零".但是,当我的变量不是一个array或一个简单int或double值,但它是一个更"复杂"的类型时,它是如何工作的?
在这个例子中,我有一个Shoplist带有vector成员变量的类.我是否必须vector在移动赋值运算符和构造函数中调用类的析构函数?或者是什么?
class Shoplist {
public:
Shoplist() :slist(0) {};
Shoplist(const Shoplist& other) :slist(other.slist) {};
Shoplist(Shoplist&& other) :slist(0) {
slist = other.slist;
other.slist.~vector();
}
Shoplist& operator=(const Shoplist& other);
Shoplist& operator=(Shoplist&& other);
~Shoplist() {};
private:
vector<Item> slist;
};
Shoplist& Shoplist::operator=(const Shoplist& other)
{
slist = other.slist;
return *this;
}
Shoplist& Shoplist::operator=(Shoplist&& other)
{
slist = other.slist;
other.slist.~vector();
return *this;
}
Run Code Online (Sandbox Code Playgroud) 考虑以下代码 -
#include <algorithm>
#include <iostream>
#include <vector>
int main()
{
std::vector<int> d {100, 200, 300};
std::vector<int> l {1, 2, 3, 4, 5};
std::move(d.begin(), d.end(), std::inserter(l, l.begin()));
for (int n : l) std::cout << n << ' ';
std::cout << '\n';
for (int n : d) std::cout << n << ' ';
std::cout << '\n\n';
for (int &n : d) n +=5;
for (int n : l) std::cout << n << ' ';
std::cout << '\n';
for (int n : …Run Code Online (Sandbox Code Playgroud) 这不会编译。为什么?
#include <iostream>
#include <vector>
struct test_s {
int a;
test_s& operator=(test_s &&ts) {
a = ts.a;
ts.a = 0;
return *this;
}
};
int main ()
{
std::vector<test_s> v;
test_s ss = std::move(v.front());
return 0;
}
Run Code Online (Sandbox Code Playgroud)
错误:
source_file.cpp:20:10: error: call to implicitly-deleted copy constructor of 'test_s'
test_s ss = std::move(v.front());
^ ~~~~~~~~~~~~~~~~~~~~
source_file.cpp:9:13: note: copy constructor is implicitly deleted because 'test_s' has a user-declared move assignment operator
test_s& operator=(test_s &&ts) {
^
1 error generated
Run Code Online (Sandbox Code Playgroud)
是否可以从矢量移动对象(无需调用复制赋值运算符)?