在使用Microsoft Visual Studio的MSVC编译器进行编译时,我的C代码遇到了一个奇怪的问题,我试图根据C标准确定这是编译器错误还是未定义的行为。
最小可重现性示例如下:
#include <utility>
#include <iostream>
#include <memory>
#include <expected>
struct UniqueHandle {
UniqueHandle() {
val = nullptr;
}
~UniqueHandle() {
std::cout << "~UniqueHandle(" << val << ")" << std::endl;
}
UniqueHandle(void* val) : val(val) {}
UniqueHandle(UniqueHandle&& other) {
std::swap(val, other.val);
}
UniqueHandle& operator=(UniqueHandle&& other) {
std::swap(val, other.val);
return *this;
}
void* val;
};
std::expected<UniqueHandle, int> foo() {
return UniqueHandle {};
}
int main() {
auto exp_res = foo();
UniqueHandle result {};
if (exp_res) {
result = std::move(exp_res.value());
}
return 0;
}
字符串
该程序使用VS 2022工具链生成以下输出:
~UniqueHandle(CCCCCCCCCCCCCCCC)
~UniqueHandle(0000000000000000)
~UniqueHandle(0000000000000000)
型
在发布版本中,第一行是一些垃圾值。我看不出这个值是从哪里来的。但是,当用g++编译时,它会像预期的那样工作,这里有一个活生生的例子:
https://gcc.godbolt.org/z/zMh5W6MMj
任何见解或解释将不胜感激!
2条答案
按热度按时间wvmv3b1j1#
在你的移动构造函数
this->val
是未初始化的。因此std::swap
将other.val
设置为这个未初始化的值。MSVC的调试运行时将未初始化的内存设置为0xCCCCCCCC
,所以这是打印的值。你可以通过添加一个成员初始化器在GCC中看到同样的行为:https://gcc.godbolt.org/z/hMa9Ka9cbMove构造函数可以用
std::exchange
来实现,以帮助避免这个问题:字符串
通过始终使用成员初始化器,可以更清楚地表明所有成员在使用前都已初始化,并且明确声明了moved from成员的新值。
w9apscun2#
公认的答案是完美的解决方案。但在一般情况下,我会使用default+swap成语(我自己编的成语):
字符串
这是一个委托构造函数的用例。