十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

c++隐式移动构造、强制拷贝省略、返回具名局部变量

c++隐式移动构造、强制拷贝省略、返回具名局部变量 翻译隐式移动implicit move on return强制要求拷贝省略mandatory copy elision一、问题“代码段std::stringmake(){returnstd::string(hello);}std::string amake();// ①std::string ba;// ②std::string cstd::move(a);// ③① make() 是 prvalue右值。编译器在重载决议时看到实参是右值自动挑中 string(string) 移动构造。你没写 std::move但移动发生了 → 这就是隐式移动。”这一段说得对吗“自动挑中 string(string) 移动构造”说的是“std::string(“hello”)”吗简短回答这段话大方向没错但不够严谨而且你问的那个细节理解反了——先说这个。“自动挑中 string(string)” 说的是谁不是std::string(hello)这一步。std::string(hello)这行代码本身调用的永远是string(const char*)构造函数——它负责把字符串内容 “hello” 构造出来分配内存、拷贝字符等。这一步跟移动构造完全无关不管上下文如何都不会变。“移动构造string(string)” 指的是另外一步当这个刚构造好的临时对象要被转移给别的对象比如函数的返回值位置或者变量a时用哪个构造函数去做这个转移动作。移动构造函数做的事情是偷走临时对象内部的堆指针而不是重新分配内存拷贝内容。所以完整来看应该是两步string(const char*)构造出临时对象内容是 “hello”把这个临时对象的资源转移给目标位置——这一步理论上会调用string(string)但这里有个更关键的问题①在 C17 下根本不会调用移动构造这段材料说自动挑中移动构造这个说法对应的是C11/14 时代或者关掉编译器优化-fno-elide-constructors时的行为。但从C17 起对于返回 prvalue纯右值的情况标准强制要求拷贝省略mandatory copy elision这不是优化开关能关掉的是语言规则本身。具体到你的例子std::stringmake(){returnstd::string(hello);}std::string amake();在 C17 下return std::string(hello);临时对象直接在返回值的位置构造没有先造出来再搬运这一说std::string a make();make()返回的 prvalue 直接在a的内存位置上构造从头到尾只发生一次string(const char*)构造调用移动构造函数根本没被调用。甚至语言标准都不要求移动构造函数是可访问的因为反正不会用到它。所以严格讲“编译器自动挑中了移动构造并调用它这句话在 C17 语境下是不准确的——准确说法应该是这里发生的是强制拷贝省略连移动构造都省了”。真正能演示隐式移动规则的例子隐式移动这个概念真正适用的场景是返回一个具名局部变量而不是直接返回一个临时量std::stringmake(){std::string localhello;returnlocal;// local 是具名左值但作为返回值时按右值处理}这里如果编译器没做 NRVO比如某些编译配置下标准规定重载决议阶段会把local当成右值来看待从而优先选中移动构造函数找不到才退化为拷贝构造——这才是标准里说的隐式移动implicit move on return的确切含义且这个规则依赖于确实存在且可访问的移动构造函数。你例子里真正明确调用移动构造的是③std::string cstd::move(a);// ③std::move(a)把左值a转成右值引用类型重载决议无歧义地选中string(string)——这个才是百分百会发生、且能观察到的移动构造调用。小结步骤实际发生什么①a make()C17 下是强制拷贝省略没有移动构造调用②b a拷贝构造string(const string)③c std::move(a)明确调用移动构造string(string)如果这段材料是较早期C11 语境的解释把①说成隐式移动是当时教科书里常见的简化说法但放到今天的编译器标准下就不严谨了容易让人以为移动构造函数真的被跑了一次实际上它压根没被调用。
返回列表