在C 中列出初始化,initializer_list和相关问题

List initialization, Initializer_list and related questions in C++

本文关键字:list 问题 initializer 初始化      更新时间:2023-10-16

我知道这是一个在stackoverflow上进行了广泛讨论的主题,但是我很难找到彻底的答案,以消除我在c 中的listibalize initialization和initializer_lists of clientibal interization offersion的彻底答案。因此,我将尝试一下自己的问题。

请考虑以下代码段:

class C
{
public :
    C(int a, int b, int c) : _a (a), _b(b), _c(c) {}; //initialization_list with ()
    //C(int a, int b, int c) : _a{ a }, _b{ b }, _c{ c } {}; //initialization list with {}
private :
    int _a, _b, _c;
};
int main()
{
    C a(5.3,3.3,4.3); // no list
    C b{5.3,3.3,4.3}; // list {}
    C c({5.3,3.3,4.3}); // list {}
}

我不明白为什么这两个初始化列表的行为类似?我期望在尝试使用类型_a{a}, _b{b}, _c{c}的初始化列表创建C类型A的对象A时,以获取有关缩小的编译器错误。但是,没有生成错误,_a, _b and _c只需存储整数值即可。

仅当使用列表" {}"创建对象B或C时,编译器才能生成狭窄的错误消息。这是为什么?使用{}或()编写初始化列表之间有任何区别,或者行为相同?

是我的下一个问题:

class C
{
public :
//private :
    int _a, _b, _c;
};
int main()
{
    C a(5,3,4); //obviously doesn't work as no such constructor
    C b{5,3,4}; //work only if _a, _b and _c are not private nor protected!
}

第二个语句(带有括号)如何才能在变量是公共的情况下起作用?涉及什么机制?

所以我想更好地理解,除了" narrating Safety" 通过使用列表{}创建对象提供的其他"功能" 提供此列表机制提供?因为在第二个呼叫中,仍然是称为默认的构造函数(因此,不是将initializer_list作为参数的默认构造函数),对吗?

最后,想象一下我的class C中,我还有另一个构造函数以参数为参数。

class C
    {
    public :
        C() = default; //default constructor
        C(int a, int b, int c) : _a (a), _b(b), _c(c) {};
        C(std::initializer_list<int> a) { //do some stuffs with the list};
    private :
        int _a, _b, _c;
    };

很明显,如果试图创建一个对象,但将任何数量的整数(实际上为0)(实际上为0),则将调用服用initializer_list的构造函数。但是,如果创建这样的对象:

C c();

C c{};

将调用默认构造函数。但是,如果用 3 整数创建对象:

C c(5,2,3);

C c{5,2,3};

将调用initializer_list构造函数。规则是这样的:

  • 如果可以调用默认的构造函数或初始化列表构造函数,请选择默认构造函数
  • 如果可以调用初始化列表和"普通构造函数",请更喜欢初始化器列表构造函数

因此(如果我错了,请纠正我),如果我这样创建我的对象:

C c{5,3,4};

将调用Intiatializer-list构造函数。但是,如果我这样创建我的对象:

C c(5,3,4);

将调用第二个构造函数(以参数为3个INT)。我的问题是:如果我还想提供狭窄的安全性,我该如何使用第二个构造函数而不是Intiatializer-list列表来创建一个对象?(因为如果我在这个问题的第一个示例中这样做,则将调用初始化列表构造函数!)。

请随时检查您的答复,并讨论与列表相关的概念,我在这个问题中尚未谈论。我想对那些很好地掌握。谢谢。

因此,随时都使用卷曲括号,您使用的是汇总初始化,一种初始化方法的结构或类的初始化方法,或者是通过指定器进行初始化的。例如,

#include <iostream>
struct Foo
{
  int a;
  char b;
};
class Doo
{
public: 
  double h;
  char ch;
};
int main() {
  Foo first = {3, 't'};
  std::cout << first.b << "n";
  //t
  Doo second = {'3', 50};
  std::cout << second.ch << "n";
  //2 interpreted as char
}

在这里,当我们使用{}初始化类或结构时,它们总是被解释为在类中列出的顺序中。这就是为什么" 2"在ASCII中印刷了" 2"的原因,对应于字符'2'。

构造函数初始化

因此,您还可以使用构造函数初始化列表的相同逻辑,

#include <iostream>
struct Pair
{
  int first;
  long second;
};
class Couple
{
public:
  Pair p;
  int g;
public:
 Couple(): p{3, 700}, g{3}
 {}
};
int main() {
  Couple test;
  std::cout << test.p.first << "n";
  //3
}

在这里,p旁边的{3, 700}Pair p = {3, 700};在代码中使用的其他位置相同。您基本上使用秩序的汇总初始化。现在,如果我们将配对字段的卷曲括号更改为括号呢?

会发生什么?

我们得到此错误

main.cpp: In constructor 'Couple::Couple()':
main.cpp:15:26: error: no matching function for call to 'Pair::Pair(int, int)'
  Couple(): p(3, 700), g{3}

那是因为我们没有一个接受两个数字的构造函数。因此,汇总初始化和括号之间的关键区别是,您需要对您使用括号进行的任何特定参数实现构造函数,但是使用卷发括号,您只需使用默认的编译器即可将编译器递给您。

std :: initializer_list是一种不常见的容器形式,用于带有{}初始化列表中的多个参数。