【发布时间】:2022-06-19 03:50:55
【问题描述】:
我从here 了解到std::initializer_list 不需要分配堆内存。这对我来说很奇怪,因为您可以在不指定大小的情况下接收 std::initializer_list 对象,而对于数组,您总是需要指定大小。尽管初始化列表在内部几乎与数组相同(正如帖子所暗示的那样)。
我很难理解的是,使用 C++ 作为静态类型语言,每个对象的内存布局(和大小)必须在编译时固定。因此,每个std::array 都是另一个类型,我们只是从一个通用模板中生成这些类型。但是对于std::initializer_list,这条规则显然不适用,因为接收函数或构造函数不需要考虑内存布局(虽然它可以从传递给其构造函数的参数派生)。仅当类型堆分配内存并且仅保留存储以管理该内存时,这对我来说才有意义。那么区别就很像std::array 和std::vector,后者也不需要指定大小。
然而 std::initializer_list 不使用堆分配,正如我的测试所示:
#include <string>
#include <iostream>
void* operator new(size_t size)
{
std::cout << "new overload called" << std::endl;
return malloc(size);
}
template <typename T>
void foo(std::initializer_list<T> args)
{
for (auto&& a : args)
std::cout << a << std::endl;
}
int main()
{
foo({2, 3, 2, 6, 7});
// std::string test_alloc = "some string longer than std::string SSO";
}
这怎么可能?我可以为自己的类型编写类似的实现吗?每当我演奏编译时管弦乐队时,这真的可以让我免于烧毁我的二进制文件。
编辑:我应该注意,我想问的问题不是编译器如何知道它应该用什么大小来实例化初始化列表(可以通过模板参数推导来实现),而是它如何那么它与初始化列表的所有其他实例没有不同的类型(因此您可以将不同大小的初始化列表传递给同一个函数)。
【问题讨论】:
-
这是可能的,因为编译器会这样做。
std::initializer_list无法在标准 C++ 中实现。 -
大小在编译时是固定的,因为只有在元素数量已知的情况下才能构造和使用它。
-
您存储在
std::initializer_list中的值就像是底层的简单数组。如果您检查为您的示例 (godbolt.org/z/vEoc46Pn9) 生成的 asm,您可以看到您的数组在二进制文件中。您无法实现它,因为std::initializer_list是编译器“绑定”的特殊类。就像constexpr construt_at一样,你也不能实现... -
这类似于您不需要在
int a[] = {1,2,3};中指定大小 - 编译器知道。 -
想想
const char s[] = "Hello World";是如何以同样的方式工作的,而s则衰减为一个简单的指针。或const char *s = "Hello World";.
标签: c++ types heap-memory stack-memory stdinitializerlist