提问人:Steven 提问时间:4/29/2023 最后编辑:Steven 更新时间:4/29/2023 访问量:88
我可以保留 std::initializer_list<T>::begin() 返回的地址吗?
Can I hold onto the address returned by std::initializer_list<T>::begin()?
问:
我有许多一类数据的静态实例,它们保留整数数组,如下所示:
class ReadableIds
{
public:
const int * ids;
ReadableIds( const int * _ids ) : ids(_ids) { }
// ... a bunch of methods that operate on ids, not germane to the discussion
};
我目前在 filescope 中按以下方式填充它们:
foo.cpp:
//
static const int phase_one_ids[] = { 1, 10, 3, 2, -1 };
static ReadableIds phase_one( phase_one_ids );
static const int phase_two_ids[] = { 31, 11, 23, 542, 11, 88, -1 };
static ReadableIds phase_two( phase_two_ids );
但是,出于易读性的原因,我真的希望它们被内联定义(该类因传递给构造函数的其他参数而更加复杂)
static ReadableIds phase_one( { 1, 10, 3, 2, -1 }, ... other args );
static ReadableIds phase_two( { 31, 11, 23, 542, 11, 88, -1 }, ... other args );
我可以编译它的唯一方法是引入 std::initializer_list:
class ReadableIds
{
public:
ReadableIds(const std::initializer_list<int> & _ids ) :
ids(_ids.begin()) // take address of initializer_list !!
{
}
};
但我的理解是 std::initializer_list 是暂时的,因此持有该数据的地址是错误的。
问:在不动态分配内存的情况下,是否有解决方案可以实现更好的语法?执行复制会导致静态内存消耗和数据复制,在我的用例中,这是不可行的。
答:
不,你不能存储 的结果,或者更确切地说:你可以存储它,但不能在初始化它成为悬空指针后取消引用它。_ids.begin()
phase_two
您的要求似乎是
ReadableIds
实际上并不存储元素本身。- 无需添加命名变量来存储元素。
- 元素也没有动态存储持续时间。
然后,唯一的方法是将元素存储在临时数组对象中。问题在于,临时性通常在它们显现出来的充分表达之后被摧毁,这不是你想要的。
std::initializer_list
构造可以将生存期延长到对象本身的生存期,但是如果您不想命名变量,那么您现在什么也没做,而是将问题转移到延长临时对象的生存期上。std::initializer_list
std::initializer_list
将临时对象的生存期延长到类对象生存期的唯一方法是直接在类中存储对临时对象的引用,并使用新出现的临时通过支撑聚合初始化立即对其进行初始化。
因此,您可以执行以下操作:
// DON'T USE THIS!
class ReadableIds
{
public:
// must be aggregate, no constructors!
std::initializer_list<int>&& _ids; // must be reference!
// other aggregate elements
};
现在您可以使用语法
// DON'T USE THIS!
// must be braces!
static ReadableIds phase_one{ { 1, 10, 3, 2, -1 },
/*initializers for other aggregate elements*/ };
并且引用的数组将存活到 ,但如果被复制/移动,则不会被复制或延长其生存期!副本将引用相同的数组和对象!显式复制/移动也不会复制/移动或延长底层数组的生命周期!_ids
phase_one
phase_one
std::initializer_list
std::initializer_list
特别是,如果不是静态存储持续时间对象,则数组不会一直存在到程序结束,只会一直存在到声明的作用域结束。phase_one
phase_one
如果使用括号而不是大括号进行初始化或插入构造函数,则生存期延长也不起作用。phase_one
总而言之,我不推荐这个。这是非常脆弱的,不值得避免命名一个额外的变量。(我也不确定编译器是否正确实现了嵌套生存期扩展的这种特殊情况。
此外,重新考虑第一个要求是否真的是必要的。很容易使类成为自动推断内部数组的正确大小的模板。
一个稍微安全的变体是使用数组而不是 .在这种情况下,您需要根据数组的大小对类进行模板化:std::initializer_list
// DON'T USE THIS!
template<std::size_t N>
class ReadableIds
{
public:
// must be aggregate, no constructors!
int (&&_ids)[N]; // must be reference!
// other aggregate elements
};
对初始化的要求仍然适用,但错误复制的可能性较小。_ids
上一个:如何使用 GCC 初始化静态变量
下一个:cpp 类声明中类常量的定义
评论
ReadableIds
vector
std::initializer_list<int> _ids
_ids
_ids
template<std::size_t N> class ReadableIds { public: std::array<int, N> my_ids; ReadableIds(std::array<int, N> const& ids) : my_ids(ids) {} };
现在,您在 .my_ids
auto x = ReadableIdx(std::array{1,2,3});