【发布时间】:2022-07-19 21:58:56
【问题描述】:
简短的回答是,使用大括号括起来的列表 foo({2, 3, 4, 5, 6}); 调用函数在概念上在调用之前在堆栈空间中创建一个临时数组,然后传递初始化列表(如 string_view)仅引用此本地临时数组(可能在寄存器中):
int __tmp_arr[5] {2, 3, 4, 5, 6};
foo(std::initializer_list{arr, arr + 5});
现在考虑以下情况,其中我嵌套了对象 \"ref\" 的 initializer_lists。此 ref 对象在变体中递归地存储基元类型或 initializer_list。我现在的问题是:这是未定义的行为吗?它似乎适用于我的代码,但它符合标准吗?我怀疑的原因是,当内部构造函数调用嵌套的大括号括起来的列表返回时,初始化列表所引用的临时数组可能会因为堆栈指针被重置而失效(因此将 initializer_list 保存在变体中会保留一个无效的目的)。然后写入后续内存将覆盖初始化列表引用的值。我相信这个有错吗?
#include <variant>
#include <string_view>
#include <type_traits>
#include <cstdio>
using val = std::variant<std::monostate, int, bool, std::string_view, std::initializer_list<struct ref>>;
struct ref
{
ref(bool);
ref(int);
ref(const char*);
ref(std::initializer_list<ref>);
val value_;
};
struct container
{
container(std::initializer_list<ref> init) {
printf(\"---------------------\\n\");
print_list(init);
}
void print_list(std::initializer_list<ref> list)
{
for (const ref& r : list) {
if (std::holds_alternative<std::monostate>(r.value_)) {
printf(\"int\\n\");
} else if (std::holds_alternative<int>(r.value_)) {
printf(\"int\\n\");
} else if (std::holds_alternative<bool>(r.value_)) {
printf(\"bool\\n\");
} else if (std::holds_alternative<std::string_view>(r.value_)) {
printf(\"string_view\\n\");
} else if (std::holds_alternative<std::initializer_list<ref>>(r.value_)) {
printf(\"initializer_list:\\n\");
print_list(std::get<std::initializer_list<ref>>(r.value_));
}
}
}
};
ref::ref(int init) : value_{init} { printf(\"%d stored\\n\", init); }
ref::ref(bool init) : value_{init} { printf(\"%s stored\\n\", init ? \"true\" : \"false\"); }
ref::ref(const char* init) : value_{std::string_view{init}} { printf(\"%s stored\\n\", init); }
ref::ref(std::initializer_list<ref> init) : value_{init} { printf(\"initializer_list stored\\n\", init); }
int main()
{
container some_container = { 1, true, 5, { {\"itemA\", 2}, {\"itemB\", true}}};
}
输出:
1 stored
true stored
5 stored
itemA stored
2 stored
initializer_list stored
itemB stored
true stored
initializer_list stored
initializer_list stored
---------------------
int
bool
int
initializer_list:
initializer_list:
string_view
int
initializer_list:
string_view
bool
-
您应该删除不相关的代码。您问的内容与
std::variant无关。 -
让我们这样说吧:我只会在
std::initializer_list在范围内时使用它,就像任何其他局部变量一样。 -
存储该列表可能不是 UB,但在“源”超出范围后访问其成员几乎可以肯定是。
-
@AdrianMole 那是我的问题。是执行容器构造函数时源超出范围?
-
临时生命周期以完整表达结束。
标签: c++ c++17 undefined-behavior initializer-list variant