在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)
我认为内部函数,右值引用和值的行为类似,唯一的区别是在值的情况下,函数本身负责销毁,而对于右值引用,责任在外面.
在标准中对待它们的动机是什么?
请考虑以下代码:
#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) 假设你想利用移动语义,但你的一个可移动类需要成为一部分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?或者我的编译器是否已过时?
移动无法在std :: array上高效实现(O(1)),为什么它有移动构造函数?
使用std::vector::emplace_back和std::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) 请考虑以下示例:
#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++ 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) 根据标准,
如果类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类成员之类的类的使用的(正当的)增加使得不可复制的类比以前更常见.
我有一个外部库,我无法修改.该库声明了一个模板函数,该函数由于某种原因返回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)
失败了
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; ^~~
这是有道理的,并没有简单的解决方法使代码编译.
但是,如果我添加一些更复杂的解决方法:
bar buz() {
return const_cast<bar&&>(std::move(foo<bar>()));
}
Run Code Online (Sandbox Code Playgroud)
它编译并且整个代码按预期工作(不仅是上面的简化示例,而且我的真实代码也是如此).
但是,它是安全的,还是我遇到了一些未定义的行为?有没有更好的解决方法?
我已阅读并理解有关返回的问题const从功能(1,2),以及常见的答案似乎是返回const对象在现代C++气馁,但我的问题是不是这件事,但我如何能解决该情况当外部库返回const对象时.
请考虑以下代码:
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)