delete又未完全delete
作者:互联网
2026-03-05
反直觉C++, 欢迎github star
delete又未完全delete
假设我们要定义一个这样的类:只支持拷贝操作不支持移动操作。
#include
class Test {
public:
Test() {
std::cout << "constructorn";
}
Test(const Test& test) {
std::cout << "copy constructorn";
}
Test& operator=(const Test& test) {
std::cout << "copy assignment operatorn";
return *this;
};
Test(Test&& test) = delete;
Test& operator=(Test&& test) = delete;
};
Test getTest() {
Test test;
return test;
}
int main() {
Test test{ getTest() };
return 0;
}
输出
main.cpp: In function 'Test getTest()':
main.cpp:16:12: error: use of deleted function 'Test::Test(Test&&)'
16 | return test;
| ^~~~
main.cpp:10:5: note: declared here
10 | Test(Test&& test) = delete;
| ^~~~
main.cpp:16:12: note: use '-fdiagnostics-all-candidates' to display considered candidates
16 | return test;
| ^~~~
从编译错误信息可以看出,被显式delete的Test(Test&& test)参与到了重载决议,且被认为比拷贝构造版本更好,但移动构造版本被指定了删除不能被使用,所以编译器报错。
指定了删除,却未完全删除,参与到了重载决议,有点反直觉。
奇怪的知识增加了
删除的移动构造函数仍然是一个已声明的函数,有资格进行重载解析。getTest函数按值返回将优先选择删除的移动构造函数而非未删除的复制构造函数。
#include
class Test {
public:
Test() {
std::cout << "constructorn";
}
Test(const Test& test) {
std::cout << "copy constructorn";
}
Test& operator=(const Test& test) {
std::cout << "copy assignment operatorn";
return *this;
};
Test(Test&& test) = default;
Test& operator=(Test&& test) = delete;
};
Test getTest() {
Test test;
return test;
}
int main() {
Test test{ getTest() };
return 0;
}
输出
constructor
#include
class Test {
public:
Test() {
std::cout << "constructorn";
}
Test(const Test& test) {
std::cout << "copy constructorn";
}
Test& operator=(const Test& test) {
std::cout << "copy assignment operatorn";
return *this;
};
Test(Test&& test) {
std::cout << "move constructorn";
}
Test& operator=(Test&& test) = delete;
};
Test getTest() {
Test test;
return test;
}
int main() {
Test test{ getTest() };
return 0;
}
输出
constructor
将Test(Test&& test) = delete修改为Test(Test&& test) = default或者显式定义虽可以修复此问题,然而,之前的本意就是将移动构造删除,让其使用拷贝构造啊,如此修改违背了本意。
更进一步的观察,竟然未输出move constructor!重载决议选择了移动构造,但未执行移动构造,有点反直觉,此问题请参考尾返回值优化。
C++11之前可以通过“只声明不实现”的方式要删除函数,既然尾返回值优化可以优化掉拷贝构造的调用,那我们可以保证编译通过即可。
#include
class Test {
public:
Test() {
std::cout << "constructorn";
}
Test(const Test& test) {
std::cout << "copy constructorn";
}
Test& operator=(const Test& test) {
std::cout << "copy assignment operatorn";
return *this;
};
Test(Test&& test);
Test& operator=(Test&& test) = delete;
};
Test getTest() {
Test test;
return test;
}
int main() {
Test test{ getTest() };
return 0;
}
输出
constructor
从上述代码表现来看,移动构造只参与了重载决议,未参与链接和运行期运行!
最后我们再尝试一个移动构造参与链接器链接的版本:
#include
class Test {
public:
Test() {
std::cout << "constructorn";
}
Test(const Test& test) {
std::cout << "copy constructorn";
}
Test& operator=(const Test& test) {
std::cout << "copy assignment operatorn";
return *this;
};
Test(Test&& test);
Test& operator=(Test&& test) = delete;
};
Test getTest() {
Test test;
return std::move(test);
}
int main() {
Test test{ getTest() };
return 0;
}
输出
/tmp/cc3TH4WE.o: In function `getTest()':
main.cpp:(.text+0x32): undefined reference to `Test::Test(Test&&)'
collect2: error: ld returned 1 exit status
重载决议选择了移动构造,但是链接时未找到移动构造的实现!
相关推荐
专题
+ 收藏
+ 收藏
+ 收藏
+ 收藏
+ 收藏
+ 收藏
最新数据
相关文章
现代Qt开发教程(新手篇)1.4——容器
C++ 类型萃取:重生之我在幼儿园修炼类型学
C++ 类型擦除:你对象是 Circle 还是 int 不重要,能 draw() 就行,我不挑
# Vcpkg使用总结2
UE5.6 Cesium 插件编译踩坑记录(UE 5.6 + MSVC 14.38 + CMake 3.31)
Emscripten 从 JavaScript 调用 C/C++
C++学习笔记(38):封装、继承、多态
C++ 内存管理:分区、自定义分配器、常见问题与检测工具
从 Unix Mini Shell 到 Browser Playground:我把 fork/exec/pipe 做成了一个可交互 Web 产品
信奥崔老师:三目运算 (Ternary Operator)
AI精选
