STD :: inditializer_list如何被允许不指定大小并同时分配堆栈?

发布于 2025-02-01 04:38:16 字数 1360 浏览 3 评论 0原文

我从这里不需要分配内存。这对我来说非常奇怪,因为您可以使用std :: prinitizer_list对象,而无需指定大小,而对于数组,您始终需要指定大小。这是虽然初始化器在内部列出的列表几乎与数组(如帖子所建议)。

我很难将头缠住的是,将C ++作为一种静态键入语言,每个对象的内存布局(和大小)必须在编译时固定。因此,每个std :: array是另一个 type ,我们刚刚从一个共同的模板中产生了这些类型。但是对于std :: onirentizer_list,显然不适用此规则,因为内存布局(虽然可以从传递给其构造函数的参数派生),而不需要考虑接收功能或构造函数。仅当类型堆放内存并仅将存储保留到 manage 时,这对我来说才有意义。然后,差异很像std :: arraystd :: vector,在以后的情况下,您也不需要指定大小。

但是std :: prinitizer_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";
}

这是怎么可能的?我可以为自己的类型编写类似的实现吗?每当我演奏编译时乐团时,这确实可以使我免于吹二进制。

edit :我应该注意,我想问的问题不是编译器如何知道它应该使用什么大小来实例化初始化列表(可以通过模板参数扣除实现),而是如何实例化。与初始化器列表的所有其他实例化的类型不同(因此,为什么您可以将不同尺寸的初始化器列表传递给同一函数)。

I understand from here that std::initializer_list doesn't need to allocate heap memory. Which is very strange to me since you can take in an std::initializer_list object without specifying the size whereas for arrays you always need to specify the size. This is although initializer lists internally almost the same as arrays (as the post suggests).

What I have a hard time wrapping my head around is that with C++ as a statically typed language, the memory layout (and size) of every object must be fixed at compile time. Thus, every std::array is another type and we just spawn those types from a common template. But for std::initializer_list, this rule apparently doesn't apply, as the memory layout (while it can be derived from the arguments passed to its constructor) doesn't need to be considered for a receiving function or constructor. This makes sense to me only if the type heap allocates memory and only reserves storage to manage that memory. Then the difference would be much like std::array and std::vector, where for the later you also don't need to specify size.

Yet std::initializer_list doesn't use heap allocations, as my tests show:

#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";
}

How is this possible? Can I write a similar implementation for my own type? That would really save me from blowing my binary whenever I play the compile time orchestra.

EDIT: I should note that the question I wanted to ask is not how the compiler knows what size it should instantiate the initializer list with (as can be achieved via template argument deduction) but how it is then not a different type from all the other instantiations of an initializer list (hence why you can pass initializer lists of different sizes to the same function).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

场罚期间 2025-02-08 04:38:16

问题是,std :: prinitizer_list不会将对象保持在自身内部。当您实例化时,编译器会注入一些其他代码,以在堆栈上创建一个临时数组,并将该数组存储到Initializer_list中的该数组中。就其价值而言,initializer_list不过是一个带有两个指针(或指针和大小)的结构:

template <class T>
class initializer_list {
private:
  T* begin_;
  T* end_;
public:
  size_t size() const { return end_ - begin_; }
  T const* begin() const { return begin_; }
  T const* end() const { return end_; }

  // ...
};

当您这样做时:

foo({2, 3, 4, 5, 6});

从概念上,这就是发生的事情:

int __tmp_arr[5] {2, 3, 4, 5, 6};
foo(std::initializer_list{arr, arr + 5});

一个较小的区别是,生活 - 数组的时间不超过initializer_list的时间。

The thing is, std::initializer_list does not hold the objects inside itself. When you instantiate it, compiler injects some additional code to create a temporary array on the stack and stores pointers to that array inside the initializer_list. For what its worth, an initializer_list is nothing but a struct with two pointers (or a pointer and a size):

template <class T>
class initializer_list {
private:
  T* begin_;
  T* end_;
public:
  size_t size() const { return end_ - begin_; }
  T const* begin() const { return begin_; }
  T const* end() const { return end_; }

  // ...
};

When you do:

foo({2, 3, 4, 5, 6});

Conceptually, here is what is happening:

int __tmp_arr[5] {2, 3, 4, 5, 6};
foo(std::initializer_list{arr, arr + 5});

One minor difference being, the life-time of the array does not exceed that of the initializer_list.

初见 2025-02-08 04:38:16

...而对于数组,您始终需要指定大小...

您的意思是吗

int a[] = {2, 3, 2, 6, 7};

我很难缠绕我的头是用C ++作为静态键入语言,每个每个语言的内存布局(和大小)必须在编译时固定。

初始化器列表的大小与compile Time 固定的与上面的数组的大小一样固定 - 它是固定的,因为您编写了Brad expression {2,3,3,2,6,7}在编译之前明确淘汰。

这怎么可能?我可以为自己的类型编写类似的实现吗?

您不能拦截支撑列表的解析,不。您可能会请参见,处理列表的规则初始化非常具体。

但是,std :: prinitizer_list旨在轻巧,因此您可以直接使用它。正如另一个答案所说,您可以将其视为一个正常的隐式大小数组,并具有隐式转换为范围式视图。

... whereas for arrays you always need to specify the size ...

You mean like for

int a[] = {2, 3, 2, 6, 7};

?

What I have a hard time wrapping my head around is that with C++ as a statically typed language, the memory layout (and size) of every must be fixed at compile time.

The size of the initializer list is just as fixed at compile time as the size of the array above - it's fixed because you wrote the braced expression {2, 3, 2, 6, 7} out explicitly before compiling.

How is this possible? Can I write a similar implementation for my own type?

You can't intercept the parsing of a braced-init-list, no. As you can see, the rules for handling list initialization are pretty specific.

However, the std::initializer_list is intended to be lightweight so you can use it directly. As the other answer says, you can consider it as a normal implicitly-sized array with an implicit conversion to a range-like view.

薆情海 2025-02-08 04:38:16

只是为了添加我自己的一些随机沉思,std :: prinitizer_list实际上是一种有趣的动物 - 它是一种嵌合体,部分stl,部分编译器构造。

如果您查看适当的STL标头文件,则会找到定义API的定义。但是实际实现实际上是编译器中内置的,因此,当您编写时,说:

std::initializer_list <int> l = { 1, 2, 3, 4, 5 };

编译器将“ a-ha!ainitializer列表”(以及int's的列表,启动),我知道那是什么,我会构建一个”。而且确实如此。在STL本身中没有代码可以执行此操作。

换句话说,std :: prinitizer_list是本地类型。只有不是,不是完全,因此是 unique 是一个非常精选的俱乐部的成员(请参阅评论)。

Just to add a few random musings of my own, std::initializer_list is actually an interesting animal - it's a kind of chimera, part STL, part compiler construct.

If you look in the appropriate STL header file, you will find a definition for it that defines the API. But the actual implementation is, in effect, built into the compiler, so when you write, say:

std::initializer_list <int> l = { 1, 2, 3, 4, 5 };

The compiler goes "a-ha! An initializer list (and a list of int's, to boot), I know what that is, I'll construct one". And so it does. There's no code to do that in the STL itself.

To put it another way, to the compiler, std::initializer_list is, in part, a native type. Only it's not, not completely, and as such it is unique a member of a very select club (please see comments).

烟酉 2025-02-08 04:38:16

您似乎有一个误解是,您需要在编译时已知的尺寸才能在堆栈上分配。实际上,没有技术原因禁止在堆栈上从运行时确定大小的对象。实际上,c明确允许使用“ nofollow noreferrer”>“变量长度”阵列。有关于此主题的另一个问题,尽管它询问

C.我不知道真正执行手动堆栈分配的理智的方法(alloca() bad 的想法,而不是真正的C ++事物),没有什么可以停止编译器为您做。

堆栈分配非常简单 - 其他人可能会纠正我,但据我所知,它归结为简单地增加堆栈指针寄存器中的值。

One misconception you seem to have is that you need a size known at compile time to allocate on the stack. In fact, there is no technical reason to disallow objects with size determined at runtime from on the stack. In fact, C explicitly allows it with Variable Length Arrays. There's another question on this topic, although it asks about C.

While I don't know of a sane C++ way to actually do manual stack allocation (alloca() being a bad idea and not really a C++ thing), nothing is stopping the compiler from doing it for you.

Stack allocation is brutally simple - others might correct me, but to my knowledge it boils down to simply increasing the value in the stack pointer register.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文