标准是否准确定义了对象移动后我可以对其做什么?我曾经认为你用移动物体做的所有事情都可以破坏它,但这还不够.
例如,采用swap标准库中定义的函数模板:
template <typename T>
void swap(T& a, T& b)
{
T c = std::move(a); // line 1
a = std::move(b); // line 2: assignment to moved-from object!
b = std::move(c); // line 3: assignment to moved-from object!
}
Run Code Online (Sandbox Code Playgroud)
显然,必须可以分配给移动的对象,否则第2行和第3行将失败.那么移动对象我还能做些什么呢?我在哪里可以找到标准中的这些细节?
(顺便说一句,为什么它T c = std::move(a);不是T c(std::move(a));第1行呢?)
我试图理解移动语义正在寻找编译器生成的移动构造函数(复制和赋值).在Modern Effective C++中,Scott Meyers在第17项中说,如果没有声明显式的复制构造函数,编译器将生成移动构造函数,它将为non-static成员执行成员移动.
为了确认这一点,我正在尝试下面的代码:
#include <iostream>
#include <string>
using namespace std;
class A
{
private:
std::string str;
public:
A() : str("Init string")
{
cout << "Default constructor" << endl;
}
A(std::string _str) : str(_str)
{
cout << "Constructor with string" << endl;
}
std::string getString()
{
return str;
}
};
int main() {
A obj1;
A obj2("Obj2 string");
cout << endl;
cout << "obj1: " << obj1.getString() << endl;
cout << "obj2: " << obj2.getString() …Run Code Online (Sandbox Code Playgroud) 我对以下代码有两个问题:1)元素是否faces会连续?
2)插入时是否std::vector复制或移动Face f?
#include <vector>
int main()
{
struct Face {};
std::vector<Face> faces;
for (int i=0; i<10; ++i)
{
Face f;
faces.push_back (f);
}
return 0;
}
Run Code Online (Sandbox Code Playgroud) 在C++ 11中实现移动语义时,移动对象应该保持安全状态,还是只能处于"垃圾"状态?
例如,在以下C++ 11包装器示例中将原始FILE*资源实现移动构造函数的首选选项是什么?
// C++11 wrapper to raw FILE*
class File
{
FILE* m_fp;
public:
// Option #1
File(File&& other)
: m_fp(other.m_fp)
{
// "other" left in a "junk" state
}
// Option #2
File(File&& other)
: m_fp(other.m_fp)
{
// Avoid dangling reference in "other"
other.m_fp = nullptr;
}
...
};
Run Code Online (Sandbox Code Playgroud) 移动语义对RAII类很有用.它们允许一个人编程,好像一个人有价值语义而没有沉重的副本.一个很好的例子是从函数返回std :: vector.然而,使用值语义进行编程意味着,人们会期望类型的行为类似于原始数据类型.这两个方面有时似乎不一致.
一方面,在RAII中,人们会期望默认构造函数返回完全初始化的对象,或者如果资源获取失败则抛出异常.这可以保证任何构造的对象都处于有效且一致的状态(即安全使用).
另一方面,对于移动语义,当对象处于有效但未指定的状态时存在一个点.类似地,原始数据类型可以处于未初始化状态.因此,使用值语义,我希望默认构造函数在此有效但未指定的状态下创建对象,以便以下代码具有预期的行为:
// Primitive Data Type, Value Semantics
int i;
i = 5;
// RAII Class, Move Semantics
Resource r;
r = Resource{/*...*/}
Run Code Online (Sandbox Code Playgroud)
在这两种情况下,我都希望"重"初始化只发生一次.我想知道,这方面的最佳做法是什么?显然,第二种方法存在一些实际问题:如果默认构造函数在未指定状态下创建对象,那么如何编写一个获取资源的构造函数,但不采用其他参数?(标记调度会浮现在脑海中......)
编辑:一些答案质疑试图让你的类像原始数据类型一样工作的理由.我的一些动机来自亚历山大·斯捷潘诺夫(Alexander Stepanov)的"高效编程与组件"(Efficient Programming with Components),他讲述了常规类型 特别是,让我引用一下:
无论c [内置类型]中的自然惯用表达是什么,都应该是常规类型的自然惯用表达式.
他继续提供与上面几乎相同的例子.他的观点在这方面是否有效?我理解错了吗?
编辑:由于没有太多的讨论,我即将接受最高投票的答案.在默认构造函数中以"从 - 移动状态"状态初始化对象可能不是一个好主意,因为每个同意现有答案的人都不会期望这种行为.
是否安全且定义良好,第二个参数std::getline(std::istream&, std::string&)是一个引用移动的左值std::string,如果是,则该字符串是从其移出状态恢复的,因此pop_back()可以安全地调用诸如此类的方法?
更简单地说,写入getline()具有等效语义的字符串是否可以分配给该字符串?
或者更具体地说,以下(有点人为的)片段是否定义明确且正确?
std::ifstream f("foo.txt");
std::vector<std::string> lines;
for (std::string s; std::getline(f, s); lines.push_back(std::move(s)))
if (!s.empty() && s.back() == '\r')
s.pop_back();
Run Code Online (Sandbox Code Playgroud)
-march=native -O3这个片段的优化()版本使用g++并且clang++似乎按预期工作,但这当然不能保证.
我想知道这是否仅依赖于根据getline()C++ 11标准中语义的明确定义的行为,或者,如果不是,它是否由更高版本的标准明确定义,或者,如果不,如果它至少由任何/所有主要实现(G ++,Clang ++,Visual C++,英特尔C++编译器)明确定义.
注意:这不是先前问题的重复,询问是否可以安全地分配给移动的对象(是的,如果它是一个普通的或STL类型),因为getline()它不是赋值运算符.
Standard指定STL容器在开始移动后(在本例中,我们说的std::move是启用移动构造/赋值)处于有效但未指定的状态。
我相信这意味着我们只能应用不需要任何先决条件的操作。我记得这里有人在Stackoverflow上声称是真实的,经过一番检查我同意了。不幸的是,我不记得我检查了哪些资源。此外,我无法在标准中找到相关信息。
从[container.requirements.general/4]表62([tab:container.req])中,我们可以看到a.size()没有任何先决条件。这是否意味着此代码是安全的?
#include <iostream>
#include <vector>
int main() {
std::vector<int> v1 = {1, 2, 3};
std::vector<int> v2 = std::move(v1);
std::cout << v1.size(); // displaying size of the moved-from vector
}
Run Code Online (Sandbox Code Playgroud)
这是未指定什么将这个代码打印,但它是安全的?意思是,我们这里有未定义的行为吗?
编辑:如果我问其他容器,我相信这个问题不会太广泛。答案在包括STL在内的所有其他STL容器中是否一致std::string?
我无法理解真实的本质std::move
std::move(object)返回“对该对象的右值引用”。是的,我明白了。push_back(std::move(object))而不是push_back(object)避免复制。我没有明白这一点,因为它似乎与下面的示例相矛盾:#include <utility>
#include <iostream>
#include <vector>
#include <string>
#include <iomanip>
template <class T> void print(T const & object){
for (auto var : object){std::cout << ' '<<var;}
std::cout << '\n';
}
int main () {
std::string foo = "foo-string";
std::string bar = "bar-string";
std::vector<std::string> myvector = { "super", "vip" , "pro" , "ok" }; // capcity=size=4;
// this block is just to increase the capacity of the vector
myvector.push_back( …Run Code Online (Sandbox Code Playgroud) 我有点困惑为什么std::move(std::string)不将传递的std::string参数设置为空状态(我的意思是std::string大小为 0 及其内部缓冲区nullptr在调用后指向std::move(std::string))。这是示例代码
#include <iostream>
#include <string>
void print(std::string& str) {
std::cout << "lref\n";
std::cout << str << "\n" << std::endl;
}
void print(const std::string& str) {
std::cout << "const lref\n";
std::cout << str << "\n" << std::endl;
}
void print(std::string&& str) {
std::cout << "rref\n";
std::cout << str << "\n" << std::endl;
}
int main() {
std::string str_a = "Hello, ";
std::string str_b = "world!";
print(str_a);
print(str_b);
print(str_a + …Run Code Online (Sandbox Code Playgroud) 我试图理解移动语义的通用规则。特别是容器和包含的元素。
原因是我试图在所有权和迭代器失效的背景下理解移动。为此,我将经历一些复杂性不断增加的案例,涉及典型容器、通用包含类型T、通用g和f函数。(也许一个重要的额外细节是,f实际上可能会或可能不会执行移动操作,或者它可能在运行时是偶然的。)
这个想法是引入案例3,这是这个问题的核心。
首先是一个相当没有争议的案例,这是可以的:
std::vector<T> v(100, t);
f(std::move(v));
v = make_a_vector();
Run Code Online (Sandbox Code Playgroud)
然而,移动后使用可能是臭代码
std::vector<T> v(100, t);
f(std::move(v));
g(v);
Run Code Online (Sandbox Code Playgroud)
我想大多数人都同意上面的做法是不行的。规则是(据我所知)移动后的唯一操作应该是赋值。我认为这尤其是因为它没有记录(未定义但有效的状态)移出向量的状态是什么(或者即使它被移动了)。因此,充其量v是空的,最坏的v情况是未指定的状态,因此g可以在此范围内执行未指定的操作。
std::vector<T> v(100, t);
f(std::move(v));
v.resize(120);
Run Code Online (Sandbox Code Playgroud)
这是对的吗?这不是一项任务,但resize没有先决条件。(发现这个Can I resize a vector that was moving from?)
现在是真正棘手的情况。
std::vector<T> v(100);
h(std::make_move_iterator(v.begin()), std::make_move_iterator(v.end()));
v.resize(120);
Run Code Online (Sandbox Code Playgroud)
(这里,h是一个采用迭代器的函数,假设它隐式引用 range 。 [iterator1, iterator2))
这是正确的代码吗?原因是.resize似乎要播放、移动、交换和复制类型为移出的对象T。
总而言之,调整其元素已(可能)移出的向量的大小是否正确?
编辑:为了论证,让我们指定函数的签名,以防它们与答案相关:
template<class …Run Code Online (Sandbox Code Playgroud)