V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
leebs
V2EX  ›  C

Cpp 对象的初始化由构造函数控制,既然有 () 去调用构造函数,为什么还要有 {} 初始化呢?

  •  
  •   leebs · 2018-09-20 22:13:25 +08:00 · 2700 次点击
    这是一个创建于 2255 天前的主题,其中的信息可能已经有所发展或是发生改变。

    对于 initiailzer_list,Cpp 为什么只提供默认构造函数:cppreference
    那我来初始化一下 vector:

    vector<int> vec(initializer_list<int>());  // "expression must have class type" warning when using vec
    

    为啥又不行呢?
    为什么改成 {} 就可以了呢?

    vector<int> vec(initializer_list<int>{}); // OK
    

    {} 是脱离于构造函数之外的嘛?
    那为什么用 {} 初始化有时候也必会调用对应的构造函数呢?

    struct A{
        A(int a, int b){this->a = 2*a; this->b = 2*b;}
    
        int a;
        int b;
    };
    
    A a{1, 2}; // (2, 4)
    

    brace(list) initialization 的设计到底是为了什么?

    9 条回复    2018-09-21 02:02:44 +08:00
    AngelCriss
        1
    AngelCriss  
       2018-09-20 22:26:18 +08:00 via Android
    看了几遍都没看明白你在说啥。。
    HHehr0ow
        2
    HHehr0ow  
       2018-09-20 22:30:57 +08:00
    most vexing parse 了解一下
    v2byy
        3
    v2byy  
       2018-09-20 22:51:55 +08:00 via iPad
    大括号初始化是 cpp11 引入的统一化初始化方式。具体建议你看下 effective modern cpp
    innoink
        4
    innoink  
       2018-09-20 23:08:00 +08:00 via Android
    initializer_list<int>()
    被解析成了一个函数
    你放个 int()也是一样
    老问题了
    innoink
        5
    innoink  
       2018-09-20 23:09:20 +08:00 via Android
    被解析成了函数声明
    chengluyu
        6
    chengluyu  
       2018-09-20 23:10:51 +08:00
    Type variable();

    上面的代码究竟是初始化了 variable 还是声明了一个叫 variable 的函数?

    想想这个问题你就明白了。
    innoink
        7
    innoink  
       2018-09-20 23:44:44 +08:00
    如果你用过 tsd::function,就会知道类似于 int(char)这种是函数类型
    因此 initializer_list<int>() 被解析成了类型名,即一个参数为空,返回值为 initializer_list<int>的函数类型
    vector<int> vec(initializer_list<int>()); 则被解析成了一个 返回值为 vector<int>、参数为上一行的函数类型 的一个函数声明

    你可以试着跑一下这个程序:
    #include <iostream>
    #include <vector>
    #include <string>
    int f()
    {
    std::cout << "111";
    return 0;
    }
    std::vector<int> v(int())
    {
    std::cout << "222";
    std::vector<int> x;
    return x;
    }

    int main()
    {
    v(f);
    return 0;

    }
    gnaggnoyil
        8
    gnaggnoyil  
       2018-09-21 00:54:12 +08:00
    >那为什么用 {} 初始化有时候也必会调用对应的构造函数呢?
    对于非 aggragate,并且具有非默认构造函数的类型,list initialization 本来就会调用它们的(隐式)构造函数.
    >brace(list) initialization 的设计到底是为了什么?
    1.避免 most vexing parse.
    2.它为 aggragate(以及数组)和非 aggragate 类型提供了统一的并且有实际作用的初始化 syntax,因为 aggragate 类型所拥有的构造函数只有隐式生成的那几个,没法用来初始化其所拥有的成员变量;而至于数组则连隐式生成的构造函数都没有.在写泛型代码或者修改类的定义的时候这种统一性会特别好使.事实上如果我没记错的话 C++标准中第二个能用于初始化一个 aggragate 类型对象的成员变量的语言特性还是 C++2a 中新加入的 designated initializers.
    3.标准中明确禁止 list initialization 在参数匹配的时候基本类型的参数出现产生精度损失的 narrowing conversion,可以帮助避免很多 bug.
    4.花括号列表中的表达式其可观察行为一定是表现的好像是从左到右被依次执行的,或者更准确的说,处于列表较后面的表达式一定 sequenced after 较前面的表达式.这个特性配合 pack expansion 可以简化很多变长模板的写法.
    dangyuluo
        9
    dangyuluo  
       2018-09-21 02:02:44 +08:00
    读一下 Effective Modern C++,Item14 还是 17 来着,很详细。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2676 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 22ms · UTC 11:54 · PVG 19:54 · LAX 03:54 · JFK 06:54
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.