为何将const引用返回值std::move后仍能触发对象移动构造?
Table::get_subtable()返回内部SubTable对象的const引用,将其通过std::move传入按值接收参数的静态工厂函数from_subtable后,结果触发了移动构造(见输出)。按直觉,移动构造函数无法从const&&构造对象,应该触发编译错误或拷贝构造,想知道哪里理解有误?
代码示例
#include <iostream> struct SubTable { SubTable() = default; SubTable(SubTable&& other) { std::cout << "Moved subtable" << std::endl; } SubTable(const SubTable&) = default; static auto from_subtable(SubTable subtable) -> SubTable; }; auto SubTable::from_subtable(SubTable subtable) -> SubTable { return subtable; } struct Table { SubTable _subtable; auto get_subtable() const -> const SubTable&; }; auto Table::get_subtable() const -> const SubTable& { return _subtable; } int main() { Table hello; auto subtable = SubTable::from_subtable(std::move(hello.get_subtable())); }
输出
Moved subtable
解答
你的误解在于触发移动构造的时机:你看到的输出并不是参数传递阶段产生的,而是函数返回阶段的行为,具体拆解如下:
参数传递阶段:
std::move(hello.get_subtable())将const左值引用转换为const SubTable&&类型,但你的SubTable并没有定义接受const SubTable&&的构造函数,因此编译器会退而求其次调用默认拷贝构造函数(const SubTable&&可以隐式绑定到const SubTable&参数)。这一步没有输出,因为你给拷贝构造函数加了= default,没有自定义输出逻辑。函数返回阶段:
在from_subtable函数中,return subtable;返回的是函数的局部参数(左值)。根据C++标准,当返回的是函数的局部对象(或参数)时,编译器会自动将其视为右值,优先调用移动构造函数。这就是你看到的Moved subtable输出的来源。验证方式:
若给拷贝构造函数添加输出逻辑:SubTable(const SubTable&) { std::cout << "Copied subtable" << std::endl; }运行后会得到如下输出,清晰展示两个阶段的行为:
Copied subtable Moved subtable补充说明:
若想让const SubTable&&直接触发移动构造,需要显式定义SubTable(const SubTable&&)构造函数,但这种做法几乎没有实际意义——移动构造的核心是“窃取”对象资源,而const右值无法被修改,根本无法完成资源窃取操作,因此标准库和常规代码中几乎不会定义这类构造。
内容的提问来源于stack exchange,提问作者glades

