Почему С++ 11 не поддерживает назначенные списки инициализаторов как C99?
Рассмотрим:
struct Person
{
int height;
int weight;
int age;
};
int main()
{
Person p { .age = 18 };
}
Этот код является законным в C99, но не является законным в С++ 11.
Какое обоснование, что С++ 11 не поддерживает такую удобную функцию?
Ответы
Ответ 1
С++ имеет конструкторы. Если имеет смысл инициализировать только один член, то это может быть выражено в программе путем реализации соответствующего конструктора. Это своего рода абстракция, которую продвигает С++.
С другой стороны, назначенная функция инициализаторов больше связана с разоблачением и упрощением доступа членов к клиенту. Это приводит к тому, что у него есть человек в возрасте 18 лет (лет?), Но с ростом и весом нуля.
Другими словами, назначенные инициализаторы поддерживают стиль программирования, в котором отображаются внутренние компоненты, и клиенту предоставляется гибкость, чтобы решить, как они хотят использовать этот тип.
С++ больше заинтересован в том, чтобы вместо этого использовать гибкость на стороне конструктора, поэтому дизайнеры могут легко использовать тип правильно и трудно использовать неправильно. Включение конструктора в управление инициализацией типа является частью этого: конструктор определяет конструкторы, инициализаторы в классе и т.д.
Ответ 2
Boost фактически поддерживает поддержку назначенных Intializers, и было много предложений, чтобы добавить поддержку С++, например: n4172 и Предложение Daryle Walker о добавлении обозначения в инициализаторы. В предложениях приводится реализация c99 Назначенные инициализаторы в Visual С++, gcc и Clang утверждают:
Мы считаем, что изменения будут относительно простыми в реализации
Но стандартный комитет повторно отклоняет такие предложения, заявив:
EWG обнаружила различные проблемы с предлагаемым подходом и не считала возможным решить проблему, поскольку она была опробована много раз и каждый раз, когда она потерпела неудачу
комментарии Бена Войгта помогли мне увидеть непреодолимые проблемы с этим подходом; Дано:
struct X {
int c;
char a;
float b;
};
Какой порядок будут вызывать эти функции в c99: struct X foo = {.a = (char)f(), .b = g(), .c = h()}
? Удивительно, что в c99:
Порядок оценки подвыражений в любом инициализаторе неопределенно секвенирован [1]
(Visual С++, gcc, и у Клана, похоже, есть согласованное поведение, так как все они будут делать вызовы в этом порядке:) < забастовкa >
Но неопределенный характер стандарта означает, что если бы эти функции имели какое-либо взаимодействие, результирующее состояние программы также было бы неопределенным, и компилятор не предупредил бы вас: Есть ли способ узнать о несовместимости назначенных инициализаторов?
С++ имеет строгие требования к списку инициализаторов 11.6. 4 [dcl.init.list] 4:
В списке инициализаторов списка с привязкой к инициализации предложения инициализатора, включая все, которые являются результатом разложений пакетов (17.5.3), оцениваются в том порядке, в котором они отображаются. То есть вычисление каждого значения и побочный эффект, связанный с заданным предложением инициализатора, секвенируются перед каждым вычислением значения и побочным эффектом, связанным с любым предложением инициализатора, которое следует за ним в списке списка инициализаторов, разделенных запятыми.
So С++ поддержка потребовала бы, чтобы это было выполнено в порядке:
Прервать совместимость с предыдущим c99, Здесь требуется явное поведение, определяющее порядок выполнения назначенных Intializers. Более перспективным предложением для Основной рабочей группы является P0329R3, в котором предлагается обозначенная инициализация со следующими ограничениями:
![введите описание изображения здесь]()
[источник]
Ответ 3
Обозначенный инициализатор в настоящее время включен в С++ 20 body of work: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/p0329r4.pdf, чтобы мы могли наконец увидеть их!
Ответ 4
Немного хакерства, так что просто поделитесь для удовольствия.
#define with(T, ...)\
([&]{ T ${}; __VA_ARGS__; return $; }())
И используйте его как:
MyFunction(with(Params,
$.Name = "Foo Bar",
$.Age = 18
));
который расширяется до:
MyFunction(([&] {
Params ${};
$.Name = "Foo Bar", $.Age = 18;
return $;
}()));
Ответ 5
Два основных функции C99, что С++ 11 Lacks упоминает "Назначенные инициализаторы и С++".
Я думаю, что "назначенный инициализатор связан с потенциальной оптимизацией. Здесь я использую в качестве примера" 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();
}
Мы знали, что во время компиляции a_point.x
равно нулю, поэтому мы могли ожидать, что foo
будет оптимизирован в один printf
.
$ gcc -O3 a.c
$ gdb a.out
(gdb) disassemble foo
Dump of assembler code for function foo:
0x00000000004004f0 <+0>: sub $0x8,%rsp
0x00000000004004f4 <+4>: mov $0x4005bc,%edi
0x00000000004004f9 <+9>: xor %eax,%eax
0x00000000004004fb <+11>: callq 0x4003a0 <[email protected]>
0x0000000000400500 <+16>: xor %eax,%eax
0x0000000000400502 <+18>: add $0x8,%rsp
0x0000000000400506 <+22>: retq
End of assembler dump.
(gdb) x /s 0x4005bc
0x4005bc: "x == 0"
foo
оптимизирован только для печати x == 0
.
Для версии С++
#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();
}
И это результат оптимизированного кода сборки.
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 <[email protected]>
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 <[email protected]>
0x00000000004005ec <+44>: mov %ebx,%eax
0x00000000004005ee <+46>: pop %rbx
0x00000000004005ef <+47>: retq
Мы видим, что a_point
не является значением постоянной времени компиляции.