Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/432.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++11 为什么C++;11不支持指定的初始值设定项列表为C99?_C++11_C++_C_Initialization_C99_C++20_C++17 - Fatal编程技术网

C++11 为什么C++;11不支持指定的初始值设定项列表为C99?

C++11 为什么C++;11不支持指定的初始值设定项列表为C99?,c++11,c++,c,initialization,c99,c++20,c++17,C++11,C++,C,Initialization,C99,C++20,C++17,考虑: struct Person { int height; int weight; int age; }; int main() { Person p { .age = 18 }; } 上述代码在C99中是合法的,但在C++11中不合法 标准委员会排除对这种便捷功能的支持的理由是什么?C++有构造函数。如果只初始化一个成员是有意义的,那么可以通过实现适当的构造函数在程序中表示。这是C++的一种抽象提法。 另一方面,指定初始值设定项功能更多的是在客户端代码中

考虑:

struct Person
{
    int height;
    int weight;
    int age;
};

int main()
{
    Person p { .age = 18 };
}
上述代码在C99中是合法的,但在C++11中不合法


标准委员会排除对这种便捷功能的支持的理由是什么?C++有构造函数。如果只初始化一个成员是有意义的,那么可以通过实现适当的构造函数在程序中表示。这是C++的一种抽象提法。 另一方面,指定初始值设定项功能更多的是在客户端代码中公开并使成员易于直接访问。这导致了一些事情,比如有一个18岁但身高和体重为零的人


换句话说,指定的初始值设定项支持一种编程风格,在这种风格中,内部内容是公开的,并且客户机可以灵活地决定如何使用该类型

C++更感兴趣的是将灵活性放在类型的设计器上,这样,设计器就可以轻松地正确使用类型,而不容易错误地使用类型。让设计器控制如何初始化类型是其中的一部分:设计器确定构造函数、类内初始值设定项等。

2017年7月15日被接受为标准:
这给公司指定的初始值设定者带来了有限的支持。C.1.7[diff.decl].4对该限制进行了如下描述,给出:

struct A { int x, y; };
struct B { struct A a; };
以下在C中有效的指定初始化在C++中受到限制:

    <代码>结构A = { y=1,x=2 } <代码>在C++中无效,因为指定符必须出现在数据成员< /LI>的声明顺序中 <>代码> int ARR(3)={[1 ]=5 } <代码>在C++中无效,因为数组指定初始化不支持 <>代码>结构B b= { .x=0 } <代码>在C++中无效,因为指定符不能嵌套 结构= c= {x=1, 2 } <代码>在C++中无效,因为所有成员或不包含数据成员都必须由指定器< /LI>初始化。

对于和更早的Boost,事实上已经有了,并且已经有很多建议来增加对该标准的支持,例如:和。建议引用Visual C++、GCC和CLAN中的指定初始化器的实现: 我们相信,这些变化将相对容易实施

但标准委员会一再声明:

EWG发现了提议方法的各种问题,并且认为尝试解决问题是不可行的,因为它已经尝试了很多次,每次都失败了

帮助我看到了这种方法无法克服的问题;鉴于:

struct X {
    int c;
    char a;
    float b;
};
这些函数的调用顺序是:
struct X foo={.a=(char)f(),.b=g(),.c=h()}
?令人惊讶的是,在:

任何初始值设定项中的子表达式的求值顺序都是不确定的[]

(VisualC++)和Clang似乎有一致的行为,因为他们都会这样称呼:

  • h()
  • f()
  • g()
  • 但标准的不确定性质意味着,如果这些函数有任何交互,则生成的程序状态也将是不确定的,编译器不会警告您:

    是否有严格的初始值设定项列表要求11.6.4[dcl.初始值列表]4:

    在带括号的init列表的初始值设定项列表中,初始值设定项子句(包括由包扩展(17.5.3)产生的任何子句)按照它们出现的顺序进行计算。也就是说,在初始值设定项列表的逗号分隔列表中,与给定初始值设定项子句相关的每个值计算和副作用在与任何初始值设定项子句相关的每个值计算和副作用之前排序

    因此,支持部门要求按照以下顺序执行:

  • f()
  • g()
  • h()
  • 破坏与以前实现的兼容性。
    如上所述,这一问题已被接受的指定初始值设定人的限制所回避。它们提供了标准化的行为,保证指定初始化器的执行顺序。

    < P> C++ 11没有提到“指定Initializers和C++”。 我认为“指定初始值设定项”与潜在优化有关。这里我使用“gcc/g++”5.1作为示例

    #include <stdio.h>
    #include <stdlib.h>
    #include <assert.h>    
    struct point {
        int x;
        int y;
    };
    const struct point a_point = {.x = 0, .y = 0};
    int foo() {
        if(a_point.x == 0){
            printf("x == 0");
            return 0;
        }else{
            printf("x == 1");
            return 1;
        }
    }
    int main(int argc, char *argv[])
    {
        return foo();
    }
    
    foo
    优化为仅打印
    x==0

    < C++版本>

    #include <stdio.h>
    #include <stdlib.h>
    #include <assert.h>
    struct point {
        point(int _x,int _y):x(_x),y(_y){}
        int x;
        int y;
    };
    const struct point a_point(0,0);
    int foo() {
        if(a_point.x == 0){
            printf("x == 0");
            return 0;
        }else{
            printf("x == 1");
            return 1;
        }
    }
    int main(int argc, char *argv[])
    {
        return foo();
    }
    
    #包括
    #包括
    #包括
    结构点{
    点(int x,int y):x(x),y(y){
    int x;
    int-y;
    };
    常数结构点a_点(0,0);
    int foo(){
    如果(a_点x==0){
    printf(“x==0”);
    返回0;
    }否则{
    printf(“x==1”);
    返回1;
    }
    }
    int main(int argc,char*argv[])
    {
    返回foo();
    }
    
    这是优化汇编代码的输出

    g++ -O3 a.cc
    $ gdb a.out
    (gdb) disassemble foo
    Dump of assembler code for function _Z3foov:
    0x00000000004005c0 <+0>:    push   %rbx
    0x00000000004005c1 <+1>:    mov    0x200489(%rip),%ebx        # 0x600a50 <_ZL7a_point>
    0x00000000004005c7 <+7>:    test   %ebx,%ebx
    0x00000000004005c9 <+9>:    je     0x4005e0 <_Z3foov+32>
    0x00000000004005cb <+11>:   mov    $0x1,%ebx
    0x00000000004005d0 <+16>:   mov    $0x4006a3,%edi
    0x00000000004005d5 <+21>:   xor    %eax,%eax
    0x00000000004005d7 <+23>:   callq  0x400460 <printf@plt>
    0x00000000004005dc <+28>:   mov    %ebx,%eax
    0x00000000004005de <+30>:   pop    %rbx
    0x00000000004005df <+31>:   retq   
    0x00000000004005e0 <+32>:   mov    $0x40069c,%edi
    0x00000000004005e5 <+37>:   xor    %eax,%eax
    0x00000000004005e7 <+39>:   callq  0x400460 <printf@plt>
    0x00000000004005ec <+44>:   mov    %ebx,%eax
    0x00000000004005ee <+46>:   pop    %rbx
    0x00000000004005ef <+47>:   retq   
    
    g++-O3 a.cc
    $gdb a.out
    (gdb)分解foo
    函数_Z3foov的汇编程序代码转储:
    0x00000000004005c0:推送%rbx
    0x00000000004005c1:mov 0x200489(%rip),%ebx#0x600a50
    0x00000000004005c7:测试%ebx,%ebx
    0x00000000004005c9:je 0x4005e0
    0x00000000004005cb:mov$0x1,%ebx
    0x00000000004005d0:mov$0x4006a3,%edi
    0x00000000004005d5:xor%eax,%eax
    0x00000000004005d7:callq 0x400460
    0x00000000004005dc:mov%ebx,%eax
    0x00000000004005de:弹出%rbx
    0x00000000004005df:retq
    0x00000000004005e0:mov$0x40069c,%edi
    0x00000000004005e5:xor%eax,%eax
    0x00000000004005e7:callq 0x400460
    0x00000000004005ec:mov%ebx,%eax
    0x00000000004005ee:弹出%rbx
    0x00000000004005ef:retq
    

    我们可以看到,
    a_点
    实际上并不是编译时常量值。

    指定的初始值设定项目前包含在C++20的工作中:
    g++ -O3 a.cc
    $ gdb a.out
    (gdb) disassemble foo
    Dump of assembler code for function _Z3foov:
    0x00000000004005c0 <+0>:    push   %rbx
    0x00000000004005c1 <+1>:    mov    0x200489(%rip),%ebx        # 0x600a50 <_ZL7a_point>
    0x00000000004005c7 <+7>:    test   %ebx,%ebx
    0x00000000004005c9 <+9>:    je     0x4005e0 <_Z3foov+32>
    0x00000000004005cb <+11>:   mov    $0x1,%ebx
    0x00000000004005d0 <+16>:   mov    $0x4006a3,%edi
    0x00000000004005d5 <+21>:   xor    %eax,%eax
    0x00000000004005d7 <+23>:   callq  0x400460 <printf@plt>
    0x00000000004005dc <+28>:   mov    %ebx,%eax
    0x00000000004005de <+30>:   pop    %rbx
    0x00000000004005df <+31>:   retq   
    0x00000000004005e0 <+32>:   mov    $0x40069c,%edi
    0x00000000004005e5 <+37>:   xor    %eax,%eax
    0x00000000004005e7 <+39>:   callq  0x400460 <printf@plt>
    0x00000000004005ec <+44>:   mov    %ebx,%eax
    0x00000000004005ee <+46>:   pop    %rbx
    0x00000000004005ef <+47>:   retq   
    
    #define with(T, ...)\
        ([&]{ T ${}; __VA_ARGS__; return $; }())
    
    MyFunction(with(Params,
        $.Name = "Foo Bar",
        $.Age  = 18
    ));
    
    MyFunction(([&] {
     Params ${};
     $.Name = "Foo Bar", $.Age = 18;
     return $;
    }()));