C++ std::initializer_list 用法

C++ std::initializer_list usage

我有几个关于 initializer_list 在 C++ 中如何工作的问题。首先,假设我们有(为简单起见省略了 std::):

initializer_list<uint32_t> lst({ 5, 4, 3 }); // (*)

在Visual Studio中,我深入研究了class,发现只有这两个构造函数:

constexpr initializer_list() noexcept : _First(nullptr), _Last(nullptr) {}

constexpr initializer_list(const _Elem* _First_arg, const _Elem* _Last_arg) noexcept
    : _First(_First_arg), _Last(_Last_arg) {}

所以我的第一个问题是:上面标有 (*) 的行,背后是否有某种语法糖允许这种语法(或者可能是编译器的常规语法,以便他对其进行处理并设置一切正常)?

从class来看,一切都是constexpr所以我的结论是它都是在编译时完成的,但是我找不到关于这个问题的任何具体解释。

下一个问题与前面的结论有点矛盾,但由于我不是 100% 确定发生的事情,所以我还是要问:如果我没有明确使用 new initializer_list<int>({...});,可以initializer_list 曾经以任何方式使用动态内存(是否有特定的用例)?

同样,这是一个愚蠢的问题,但我想确切地了解 class 发生了什么,以及它如何破坏内存。

第三个问题是,假设我们有这样一段代码:

#include <iostream>
using namespace std;

class Test
{
    public: Test(initializer_list<uint32_t>&& lst)
    {
        cout << "Size: " << lst.size() << endl;

        for (const uint32_t* i = lst.begin(); i != lst.end(); ++i)
        {
            cout << " " << *i;
        }

        cout << endl << endl;
    }
};

int main(void)
{
    Test t1({ 4, 6, 3 });
    Test t2({ 6, 3, 2, 8 });

    return 0;
}

在这种情况下,除了与 t1t2 一起使用之外,是否有任何可能的方法来使用 Test class 的构造函数实例?当然,这从一开始就正确吗? cout 确实打印出正确的值,但是我想确保没有任何格式错误的东西,例如 'brace init list' { ... } 被销毁(如果说这样的话是偶数可能)在 cout 使用它们之前?

这让我很困扰,因为我不了解 C++ 在编写此类内容时如何管理内存。

1

So my first question is: the line above marked with (*), is there some kind of syntactic sugar behind that allows this syntax (or maybe regular syntax for the compiler, so that he mangles with it and sets up everything properly)?

来自 cppreference:

Notes

Despite a lack of constructors, it is possible to create non-empty initializer lists. Instances of std::initializer_list are implicitly constructed when:

  • a braced-init-list is used in list-initialization, including function-call list initialization and assignment expressions (not to be confused with constructor initializer lists)
  • a braced-init-list is bound to auto, including in a ranged for loop

2

Next question [...]: if I'm not explicitly using new initializer_list({...});, can the initializer_list ever use dynamic memory in any way (is there a certain use-case where it does)?

你为什么要这样做?它们的主要目的是将参数列表传递给构造函数。它们是一个数组的轻量级包装器,由编译器动态构建,允许您按照

行编写代码
std::string s1{'a', 'b', 'c', 'd'};

3a

In this case, is there any possible way to use the constructor of the Test class other than the way it is used with the t1 and t2 instances?

我不太明白你在这里问什么。我会这样写:

Test t1{ 4, 6, 3 };
Test t2{ 6, 3, 2, 8 };

3b

And of course, is this even correct to begin with?

你的代码没有问题。

3c

like for instance the 'brace init list' { ... } being destroyed (if saying something like this is even possible) before cout uses them?

再次来自 cppreference:

The underlying array is a temporary array of type const T[N], in which each element is copy-initialized (except that narrowing conversions are invalid) from the corresponding element of the original initializer list. The lifetime of the underlying array is the same as any other temporary object, except that initializing an initializer_list object from the array extends the lifetime of the array exactly like binding a reference to a temporary (with the same exceptions, such as for initializing a non-static class member). The underlying array may be allocated in read-only memory.

TL;DR:即使是临时代码也不会停止存在于两行随机代码之间。您可以安全地使用函数参数 lst 直到函数结束。

PS

只有在回答到一半时,我才意识到这不仅是 3 个问题,而且实际上还有更多问题。最好专注于问题中的一点。如果您有更多问题,请提出更多问题。