更改类型后丢失对 std::variant 对象的引用
Lose reference to std::variant object after changing its type
请注意,问题可以通过 运行 下面的代码片段重现(我在 gcc 9.1 中使用 wandbox)
所以我指定了两个自定义类型(Normal
和 Special
)的 std::array
(为简单起见大小为 2)std::variant
Normal
作为 class 构造时的第一种类型,数组默认构造为 Normal
个对象。我更改了数组第一个元素的一些内部数据成员并将其打印出来。看起来不错。
现在我想将数组的第二个元素设置为 Special
对象。我尝试根据本教程 (https://www.bfilipek.com/2018/06/variant.html#changing-the-values)
分配给一个新值并使用 emplace
来做到这一点
但是,当我尝试更改第二个对象(现在键入 Special
)的内部数据成员时,似乎我没有对原始数组中的对象进行操作。打印结果显示构造的默认值(在本例中为 0)我是使用 std::variant
的新手,所以我不知道为什么会这样。我怎样才能获得对数组中最近类型更改的变体对象的实际引用?
#include <iostream>
#include <memory>
#include <cstring>
#include <array>
#include <variant>
struct Normal {
struct Header {
std::array<uint8_t, 2> reserved;
};
Normal() : frame{0}, payload{reinterpret_cast<uint8_t*>(frame + sizeof(Header))} {}
constexpr static auto LENGTH = 10;
uint8_t frame[LENGTH];
uint8_t* payload;
};
struct Special {
struct Header {
std::array<uint8_t, 3> reserved;
};
Special() : frame{0}, payload{reinterpret_cast<uint8_t*>(frame + sizeof(Header))} {}
constexpr static auto LENGTH = 11;
uint8_t frame[LENGTH];
uint8_t* payload;
};
std::array<std::variant<Normal, Special>, 2> handlers;
Normal* normal_handler;
Special* special_handler;
int main() {
auto& nh = std::get<Normal>(handlers[0]);
memset(nh.payload, 3, 3);
normal_handler = &nh;
handlers[1].emplace<1>(Special{});
auto& sh = std::get<Special>(handlers[1]);
memset(sh.payload, 4 ,4);
// memset(std::get<Special>(handlers[1]).payload, 4, 4);
special_handler = &sh;
for (int i = 0; i < 10; i++) {
// Expect 3 bytes from 3rd bytes = 3
std::cout << (int) normal_handler->frame[i] << " ";
}
std::cout << std::endl;
for (int i = 0; i < 11; i++) {
// Expect 4 bytes from 4th bytes = 4
std::cout << (int) special_handler->frame[i] << " ";
// std::cout << (int) std::get<Special>(handlers[1]).frame[i] << " ";
}
}
您的问题与 std::variant
无关,以下代码显示相同的行为:
#include <iostream>
#include <memory>
#include <cstring>
struct Special {
struct Header {
std::array<uint8_t, 3> reserved;
};
Special() : frame{0}, payload{reinterpret_cast<uint8_t*>(frame + sizeof(Header))} {}
constexpr static auto LENGTH = 11;
uint8_t frame[LENGTH];
uint8_t* payload;
};
int main() {
Special s1;
s1 = Special{};
memset(s1.payload, 4 ,4);
for (int i = 0; i < 11; i++) {
// Expect 4 bytes from 4th bytes = 4
std::cout << (int) s1.frame[i] << " ";
}
}
这一行:
s1 = Special{};
创建一个临时 Special
对象,然后将其分配给 s1
。默认的复制和移动构造函数会将 s1.payload
设置为临时文件中 payload
的值。因此 s1.payload
是临时对象中指向 frame
的悬垂指针,因此您的其余代码具有未定义的行为。
最简单的修复方法是将 payload
成员更改为函数:
#include <iostream>
#include <memory>
#include <cstring>
struct Special {
struct Header {
std::array<uint8_t, 3> reserved;
};
Special() : frame{0} {}
constexpr static auto LENGTH = 11;
uint8_t frame[LENGTH];
uint8_t* payload() { return &frame[sizeof(Header)]; }
};
int main() {
Special s1;
s1 = Special{};
memset(s1.payload(), 4 ,4);
for (int i = 0; i < 11; i++) {
// Expect 4 bytes from 4th bytes = 4
std::cout << (int) s1.frame[i] << " ";
}
}
请注意,问题可以通过 运行 下面的代码片段重现(我在 gcc 9.1 中使用 wandbox)
所以我指定了两个自定义类型(Normal
和 Special
)的 std::array
(为简单起见大小为 2)std::variant
Normal
作为 class 构造时的第一种类型,数组默认构造为 Normal
个对象。我更改了数组第一个元素的一些内部数据成员并将其打印出来。看起来不错。
现在我想将数组的第二个元素设置为 Special
对象。我尝试根据本教程 (https://www.bfilipek.com/2018/06/variant.html#changing-the-values)
emplace
来做到这一点
但是,当我尝试更改第二个对象(现在键入 Special
)的内部数据成员时,似乎我没有对原始数组中的对象进行操作。打印结果显示构造的默认值(在本例中为 0)我是使用 std::variant
的新手,所以我不知道为什么会这样。我怎样才能获得对数组中最近类型更改的变体对象的实际引用?
#include <iostream>
#include <memory>
#include <cstring>
#include <array>
#include <variant>
struct Normal {
struct Header {
std::array<uint8_t, 2> reserved;
};
Normal() : frame{0}, payload{reinterpret_cast<uint8_t*>(frame + sizeof(Header))} {}
constexpr static auto LENGTH = 10;
uint8_t frame[LENGTH];
uint8_t* payload;
};
struct Special {
struct Header {
std::array<uint8_t, 3> reserved;
};
Special() : frame{0}, payload{reinterpret_cast<uint8_t*>(frame + sizeof(Header))} {}
constexpr static auto LENGTH = 11;
uint8_t frame[LENGTH];
uint8_t* payload;
};
std::array<std::variant<Normal, Special>, 2> handlers;
Normal* normal_handler;
Special* special_handler;
int main() {
auto& nh = std::get<Normal>(handlers[0]);
memset(nh.payload, 3, 3);
normal_handler = &nh;
handlers[1].emplace<1>(Special{});
auto& sh = std::get<Special>(handlers[1]);
memset(sh.payload, 4 ,4);
// memset(std::get<Special>(handlers[1]).payload, 4, 4);
special_handler = &sh;
for (int i = 0; i < 10; i++) {
// Expect 3 bytes from 3rd bytes = 3
std::cout << (int) normal_handler->frame[i] << " ";
}
std::cout << std::endl;
for (int i = 0; i < 11; i++) {
// Expect 4 bytes from 4th bytes = 4
std::cout << (int) special_handler->frame[i] << " ";
// std::cout << (int) std::get<Special>(handlers[1]).frame[i] << " ";
}
}
您的问题与 std::variant
无关,以下代码显示相同的行为:
#include <iostream>
#include <memory>
#include <cstring>
struct Special {
struct Header {
std::array<uint8_t, 3> reserved;
};
Special() : frame{0}, payload{reinterpret_cast<uint8_t*>(frame + sizeof(Header))} {}
constexpr static auto LENGTH = 11;
uint8_t frame[LENGTH];
uint8_t* payload;
};
int main() {
Special s1;
s1 = Special{};
memset(s1.payload, 4 ,4);
for (int i = 0; i < 11; i++) {
// Expect 4 bytes from 4th bytes = 4
std::cout << (int) s1.frame[i] << " ";
}
}
这一行:
s1 = Special{};
创建一个临时 Special
对象,然后将其分配给 s1
。默认的复制和移动构造函数会将 s1.payload
设置为临时文件中 payload
的值。因此 s1.payload
是临时对象中指向 frame
的悬垂指针,因此您的其余代码具有未定义的行为。
最简单的修复方法是将 payload
成员更改为函数:
#include <iostream>
#include <memory>
#include <cstring>
struct Special {
struct Header {
std::array<uint8_t, 3> reserved;
};
Special() : frame{0} {}
constexpr static auto LENGTH = 11;
uint8_t frame[LENGTH];
uint8_t* payload() { return &frame[sizeof(Header)]; }
};
int main() {
Special s1;
s1 = Special{};
memset(s1.payload(), 4 ,4);
for (int i = 0; i < 11; i++) {
// Expect 4 bytes from 4th bytes = 4
std::cout << (int) s1.frame[i] << " ";
}
}