提问人:jerin 提问时间:10/9/2023 更新时间:10/9/2023 访问量:51
具有指向 std::optional 中值的视图的值更正
Value corrruption with a view pointing to a value within an std::optional
问:
我正在尝试创建一个设置,其中 C++ 类的实例可以选择拥有数据。为此,我正在创建一个 ,以及一个视图,以显示这个可选的视图(或从外部提供的东西)。考虑以下代码片段,这是我在解决此问题时遇到的问题的最小可重现示例:std::optional<Type>
std::optional
#include <algorithm>
#include <iostream>
#include <optional>
#include <string_view>
class Wrap {
public:
Wrap(int value, size_t size) : size_(size) {
value_ = new int[size];
std::fill(value_, value_ + size_, value);
std::cout << "new Wrap(" << (void*)value_ << ", " << size_ << ")" << std::endl;
}
~Wrap() {
std::cout << "delete Wrap(" << (void*)value_ << ", " << size_ << ")" << std::endl;
delete[] value_;
size_ = 0;
}
const int *data() const { return value_; }
size_t size() const { return size_; }
private:
int *value_;
size_t size_;
};
template <class T>
struct Pair {
T first;
T second;
};
Pair<Wrap> wrap_from(int first, int second, size_t size) {
return {
.first = Wrap(first, size), //
.second = Wrap(second, size) //
};
}
Pair<std::string_view> view_from(const Pair<Wrap> &wrap) {
return {
.first = std::string_view( //
reinterpret_cast<const char *>(wrap.first.data()), //
wrap.first.size() //
), //
.second = std::string_view( //
reinterpret_cast<const char *>(wrap.second.data()), //
wrap.second.size()), //
//
};
}
class Holder {
public:
Holder(int first, int second, size_t size)
: wrap_(wrap_from(first, second, size)), view_(view_from(*wrap_)) {
std::cout << "access first(" << (void*)view_.first.data() << ", " << view_.first.size() << ")" << std::endl;
std::cout << "access second(" << (void*)view_.second.data() << ", " << view_.second.size() << ")" << std::endl;
std::cout << "first: " << *(int*)view_.first.data() << std::endl;
std::cout << "second: " << *(int*)view_.second.data() << std::endl;
}
private:
using WrapPair = Pair<Wrap>;
std::optional<WrapPair> wrap_;
Pair<std::string_view> view_;
};
int main() {
Holder holder(3, 4, 10);
return 0;
}
此代码提供以下输出(请参阅 https://godbolt.org/z/3r1MrYY7P):
Program returned: 139
Program stdout
new Wrap(0x563f294a62b0, 10)
new Wrap(0x563f294a72f0, 10)
delete Wrap(0x563f294a72f0, 10)
delete Wrap(0x563f294a62b0, 10)
access first(0x563f294a62b0, 10)
access second(0x563f294a72f0, 10)
first: 692744944
second: 0
delete Wrap(0x563f294a72f0, 10)
Program stderr
free(): double free detected in tcache 2
Program terminated with signal: SIGSEGV
我试图把我的头绕在调用析构函数的位置,使析构函数中的值变得无用。双重释放似乎也在发生。我预计,由于其中大多数都是返回类型,因此移动语义和复制省略开始发挥作用。std::optional
我做错了什么?如何缓解这种情况?有没有其他方法可以达到预期的效果?
答:
2赞
PaulMcKenzie
10/9/2023
#1
您的类违反了 3 规则。Wrap
添加缺少的功能:
Wrap(const Wrap& rhs) : value_(new int[rhs.size_])
{
std::copy(rhs.value_, rhs.value_ + rhs.size_, value_);
size_ = rhs.size_;
}
Wrap& operator=(Wrap rhs)
{
std::swap(rhs.size_, size_);
std::swap(rhs.value_, value_);
return *this;
}
运行时错误现已得到缓解。
正在完成的复制的更好说明是在构造函数和析构函数中输出 的值和其他信息:this
评论
Wrap