этот код:
#include <iostream>
struct Acc {
int a;
};
struct Buu {
int b;
};
struct Foo {
const Acc& acc;
Buu& buu;
};
void printInfo( const Foo& ) {
std::cout << "hi!" << std::endl;
}
void call( Buu& buu ) {
Acc acc = { 1 };
Foo foo = {
.acc = acc,
.buu = buu,
};
std::cout << "before" << std::endl;
printInfo( foo );
std::cout << "after" << std::endl;
}
void noCall( Buu& buu ) {
Acc acc = { 1 };
Foo foo = {
.buu = buu,
.acc = acc,
};
std::cout << "before" << std::endl;
printInfo( foo );
std::cout << "after" << std::endl;
}
int main() {
Buu buu = { 2 };
call( buu );
noCall( buu );
return 0;
}
когда компилируется clang (я пробовал 3.7.0, 3.7.1), будет:
before
hi!
after
before
after
Второй звонок printInfo
был удален … Разница между call
а также noCall
только в порядке назначенных инициализаторов.
С -pedantic
опция будет выдавать предупреждения о том, что назначенные инициализаторы являются функцией C99, но не C ++, но все же создают код без второго вызова printInfo
,
Это известная ошибка?
Я думаю, что это по крайней мере несправедливо, если не ошибка, потому что предупреждение только на уровне педантичности, когда Clang просто удаляет все ссылки на foo
в функции nocall
, Мы можем подтвердить это, посмотрев код сборки в режиме отладки (c++ -S -g file.cpp
), чтобы увидеть, как именно компилятор интерпретирует каждую строку.
Когда мы смотрим на сгенерированный файл .s, мы видим, что в вызове, строки 20 Foo foo = {...
и 25 printInfo(foo)
генерируются:
.loc 1 20 0 # ess.cpp:20:0
movq %rcx, -64(%rbp)
movq -40(%rbp), %rcx
.Ltmp45:
movq %rcx, -56(%rbp)
.loc 1 24 0 # ess.cpp:24:0
movq %rax, %rdi
callq _ZNSt3__1lsINS_11char_traitsIcEEEERNS_13basic_ostreamIcT_EES6_PKc
leaq -64(%rbp), %rdi
leaq _ZNSt3__14endlIcNS_11char_traitsIcEEEERNS_13basic_ostreamIT_T0_EES7_, %rcx
movq %rax, -24(%rbp)
movq %rcx, -32(%rbp)
movq -24(%rbp), %rax
.loc 5 322 0 # /usr/include/c++/v1/ostream:322:0
.Ltmp46:
movq %rdi, -72(%rbp) # 8-byte Spill
movq %rax, %rdi
callq *-32(%rbp)
.Ltmp47:
.loc 1 25 0 # ess.cpp:25:0
movq -72(%rbp), %rdi # 8-byte Reload
movq %rax, -80(%rbp) # 8-byte Spill
callq _Z9printInfoRK3Foo
leaq _ZNSt3__14coutE, %rdi
leaq .L.str2, %rsi
Но для примечания соответствующие строки (30 и 35) не являются:
.loc 1 29 0 prologue_end # ess.cpp:29:0
.Ltmp57:
movl .L_ZZ6noCallR3BuuE3acc, %ecx
movl %ecx, -48(%rbp)
.loc 1 34 0 # ess.cpp:34:0
movq %rax, %rdi
callq _ZNSt3__1lsINS_11char_traitsIcEEEERNS_13basic_ostreamIcT_EES6_PKc
leaq _ZNSt3__14coutE, %rdi
leaq .L.str2, %rsi
leaq _ZNSt3__14endlIcNS_11char_traitsIcEEEERNS_13basic_ostreamIT_T0_EES7_, %rdx
movq %rax, -24(%rbp)
movq %rdx, -32(%rbp)
movq -24(%rbp), %rax
.loc 5 322 0 # /usr/include/c++/v1/ostream:322:0
.Ltmp58:
movq %rdi, -72(%rbp) # 8-byte Spill
movq %rax, %rdi
movq %rsi, -80(%rbp) # 8-byte Spill
callq *-32(%rbp)
.Ltmp59:
.loc 1 36 0 # ess.cpp:36:0
movq -72(%rbp), %rdi # 8-byte Reload
movq -80(%rbp), %rsi # 8-byte Reload
movq %rax, -88(%rbp) # 8-byte Spill
callq _ZNSt3__1lsINS_11char_traitsIcEEEERNS_13basic_ostreamIcT_EES6_PKc
leaq _ZNSt3__14endlIcNS_11char_traitsIcEEEERNS_13basic_ostreamIT_T0_EES7_, %rdx
movq %rax, -8(%rbp)
movq %rdx, -16(%rbp)
movq -8(%rbp), %rdi
.loc 5 322 0 # /usr/include/c++/v1/ostream:322:0
.Ltmp60:
callq *-16(%rbp)
.Ltmp61:
.loc 1 37 0 # ess.cpp:37:0
Где пронумерованные строки в файле cpp:
18 void call( Buu& buu ) {
19 Acc acc = { 1 };
20 Foo foo = {
21 .acc = acc,
22 .buu = buu,
23 };
24 std::cout << "before" << std::endl;
25 printInfo( foo );
26 std::cout << "after" << std::endl;
27 }
28 void noCall( Buu& buu ) {
29 Acc acc = { 1 };
30 Foo foo = {
31 .buu = buu,
32 .acc = acc
33 };
34 std::cout << "before" << std::endl;
35 printInfo( foo );
36 std::cout << "after" << std::endl;
37 }
Насколько я понимаю, Clang делает вид, что обрабатывает синтаксис C99 в режиме C ++, когда это не так.
ИМХО, это ошибка, о которой можно сообщать лязгу, потому что как минимум диагностика должна быть выдана в соответствии с 1.4 Соответствием реализации [intro.compliance]
1 Набор диагностируемых правил состоит из всех синтаксических и семантических правил в этом международном стандарте Кроме
для тех правил, которые содержат явное обозначение, что «диагностика не требуется» или которые описаны как
в результате чего «неопределенное поведение».
2 Хотя в этом международном стандарте указаны только требования к реализациям C ++, эти требования
часто легче понять, если они сформулированы как требования к программам, частям программ или
выполнение программ. Такие требования имеют следующее значение:
- Если программа не содержит нарушений правил в этом международном стандарте, соответствующая реализация
в пределах своих ресурсов принимает и корректно исполняет эту программу2.- Если программа содержит нарушение любого диагностируемого правила или вхождение конструкции, описанной в
этот стандарт как «условно поддерживаемый», когда реализация не поддерживает эту конструкцию,
соответствующая реализация должна выдать хотя бы одно диагностическое сообщение.…
8 Соответствующая реализация может иметь расширения (включая дополнительные библиотечные функции), если они
не изменять поведение любой правильно сформированной программы. Реализации необходимы для диагностики программ, которые
использовать такие расширения, которые плохо сформированы в соответствии с этим международным стандартом. Сделав это, однако,
они могут компилировать и выполнять такие программы.
Других решений пока нет …