2016-04-19 6 views
1

Я знаю о преимуществах, которые имеет список инициализаторов над обычным корпусом конструктора (по умолчанию построено с последующим присваиванием вместо построения).Оптимизация компилятора для конструкторов

Я также знаю о возможностях оптимизации современных компиляторов.

Являются ли современные компиляторы достаточно умными, чтобы оптимизировать конструктор списка без инициализатора для первого типа? Если да, то ограничивается ли он только базовыми типами или включены также определенные пользователем типы? Если нет, почему бы и нет?

+0

Fyi, во многих случаях это не только выгодно; его * требуется *. Элемент 'const', ссылочный элемент или элемент, не имеющий значения по умолчанию, все * должны * участвовать в списке инициализации членов. – WhozCraig

+0

Я тоже знаю об этом. Но вопрос касается случаев, когда они действительны. – CinCout

+0

Вы действительно можете быть, но внешний читатель, видя ваше первое предложение, может * не * быть. В противном случае я бы не упомянул об этом. – WhozCraig

ответ

1

Вот как gcc5.3 обрабатывает его с -O2. Ваши подозрения верны - в тривиальных случаях оптимизатор компенсирует неаккуратное программирование.

Проблема возникает, когда компилятор не может видеть в конструкторе или операторе присваивания переменных-члена (в этом случае, поскольку они определены в другой единицы перевода).

Когда это произойдет, вы получите лучший код (по крайней мере, с GCC, и я подозреваю, что со всеми остальными), если конструкторы написаны правильно:

тестовый код:

#include <string> 

struct bar 
{ 
    bar(std::string = {}, std::string = {}); 
    bar(bar&&); 
    bar& operator=(bar&&); 
}; 

struct foo 
{ 
    __attribute__((noinline)) 
    foo(int x, double y, std::string z, std::string o, std::string p) 
    { 
    a = x; 
    b = y; 
    c = z; 
    _bar = bar(o, p); 
    } 
    int a; 
    double b; 
    std::string c; 
    bar _bar; 
}; 

struct foo2 
{ 
    __attribute__((noinline)) 
    foo2(int x, double y, std::string z, std::string o, std::string p) 
    : a(x), b(y), c(std::move(z)), _bar(std::move(o), std::move(p)) 
    { 
    } 

    int a; 
    double b; 
    std::string c; 
    bar _bar; 
}; 


int main() 
{ 
    foo f(45, 12.2, "hello", "foo", "bar"); 
    foo2 f2(45, 12.2, "hello", "foo", "bar"); 

} 

пример вывода на ассемблере:

.LC0: 
     .string "basic_string::_M_construct null not valid" 
std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(char const*, std::allocator<char> const&) [clone .isra.16]: 
     pushq %r13 
     pushq %r12 
     leaq 16(%rdi), %r12 
     pushq %rbp 
     pushq %rbx 
     subq $24, %rsp 
     testq %rsi, %rsi 
     movq %r12, (%rdi) 
     je  .L2 
     movq %rdi, %rbx 
     movq %rsi, %rdi 
     movq %rsi, %r13 
     call strlen 
     cmpq $15, %rax 
     movq %rax, %rbp 
     movq %rax, 8(%rsp) 
     ja  .L13 
     cmpq $1, %rax 
     je  .L14 
     testq %rax, %rax 
     jne  .L15 
.L6: 
     movq 8(%rsp), %rax 
     movq (%rbx), %rdx 
     movq %rax, 8(%rbx) 
     movb $0, (%rdx,%rax) 
     addq $24, %rsp 
     popq %rbx 
     popq %rbp 
     popq %r12 
     popq %r13 
     ret 
.L13: 
     leaq 8(%rsp), %rsi 
     xorl %edx, %edx 
     movq %rbx, %rdi 
     call std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_create(unsigned long&, unsigned long) 
     movq 8(%rsp), %rdx 
     movq %rax, (%rbx) 
     movq %rax, %rdi 
     movq %rdx, 16(%rbx) 
.L4: 
     movq %rbp, %rdx 
     movq %r13, %rsi 
     call memcpy 
     jmp  .L6 
.L14: 
     movzbl 0(%r13), %eax 
     movb %al, 16(%rbx) 
     jmp  .L6 
.L2: 
     movl $.LC0, %edi 
     call std::__throw_logic_error(char const*) 
.L15: 
     movq %r12, %rdi 
     jmp  .L4 
foo::foo(int, double, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >): 
     pushq %r15 
     pushq %r14 
     leaq 32(%rdi), %r14 
     pushq %r13 
     pushq %r12 
     leaq 48(%rdi), %r12 
     pushq %rbp 
     pushq %rbx 
     movl %esi, %r15d 
     movq %rdi, %rbx 
     movq %rcx, %r13 
     movq %r8, %rbp 
     subq $104, %rsp 
     movq %r14, 16(%rdi) 
     movq $0, 24(%rdi) 
     leaq 80(%rsp), %rax 
     movq %rdx, 8(%rsp) 
     leaq 32(%rsp), %rsi 
     leaq 64(%rsp), %rdx 
     movb $0, 32(%rdi) 
     movq %r12, %rdi 
     movq %rax, 64(%rsp) 
     leaq 48(%rsp), %rax 
     movsd %xmm0, (%rsp) 
     movq $0, 72(%rsp) 
     movb $0, 80(%rsp) 
     movq %rax, 32(%rsp) 
     movq $0, 40(%rsp) 
     movb $0, 48(%rsp) 
     call bar::bar(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >) 
     movq 32(%rsp), %rdi 
     leaq 48(%rsp), %rax 
     cmpq %rax, %rdi 
     je  .L17 
     call operator delete(void*) 
.L17: 
     movq 64(%rsp), %rdi 
     leaq 80(%rsp), %rax 
     cmpq %rax, %rdi 
     je  .L18 
     call operator delete(void*) 
.L18: 
     movsd (%rsp), %xmm1 
     movq 8(%rsp), %rsi 
     leaq 16(%rbx), %rdi 
     movl %r15d, (%rbx) 
     movsd %xmm1, 8(%rbx) 
     call std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_assign(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&) 
     movq 0(%rbp), %r15 
     leaq 80(%rsp), %rax 
     movq 8(%rbp), %rbp 
     movq %rax, 64(%rsp) 
     movq %r15, %rax 
     addq %rbp, %rax 
     je  .L21 
     testq %r15, %r15 
     jne  .L21 
     movl $.LC0, %edi 
     call std::__throw_logic_error(char const*) 
.L21: 
     cmpq $15, %rbp 
     movq %rbp, 16(%rsp) 
     ja  .L69 
     cmpq $1, %rbp 
     je  .L70 
     xorl %edx, %edx 
     testq %rbp, %rbp 
     leaq 80(%rsp), %rax 
     jne  .L71 
.L24: 
     movq %rdx, 72(%rsp) 
     movb $0, (%rax,%rdx) 
     leaq 48(%rsp), %rax 
     movq 0(%r13), %r15 
     movq 8(%r13), %rbp 
     movq %rax, 32(%rsp) 
     movq %r15, %rax 
     addq %rbp, %rax 
     je  .L27 
     testq %r15, %r15 
     jne  .L27 
     movl $.LC0, %edi 
     call std::__throw_logic_error(char const*) 
.L27: 
     cmpq $15, %rbp 
     movq %rbp, 24(%rsp) 
     ja  .L72 
     cmpq $1, %rbp 
     je  .L73 
     xorl %eax, %eax 
     testq %rbp, %rbp 
     leaq 48(%rsp), %rdx 
     leaq 24(%rsp), %r13 
     jne  .L74 
.L30: 
     movq %rax, 40(%rsp) 
     leaq 32(%rsp), %rsi 
     movb $0, (%rdx,%rax) 
     leaq 64(%rsp), %rdx 
     movq %r13, %rdi 
     call bar::bar(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >) 
     movq %r13, %rsi 
     movq %r12, %rdi 
     call bar::operator=(bar&&) 
     movq 32(%rsp), %rdi 
     leaq 48(%rsp), %rax 
     cmpq %rax, %rdi 
     je  .L31 
     call operator delete(void*) 
.L31: 
     movq 64(%rsp), %rdi 
     leaq 80(%rsp), %rax 
     cmpq %rax, %rdi 
     je  .L16 
     call operator delete(void*) 
.L16: 
     addq $104, %rsp 
     popq %rbx 
     popq %rbp 
     popq %r12 
     popq %r13 
     popq %r14 
     popq %r15 
     ret 
.L69: 
     leaq 16(%rsp), %rsi 
     leaq 64(%rsp), %rdi 
     xorl %edx, %edx 
     call std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_create(unsigned long&, unsigned long) 
     movq 16(%rsp), %rdx 
     movq %rax, 64(%rsp) 
     movq %rax, %rdi 
     movq %rdx, 80(%rsp) 
.L22: 
     movq %rbp, %rdx 
     movq %r15, %rsi 
     call memcpy 
     movq 16(%rsp), %rdx 
     movq 64(%rsp), %rax 
     jmp  .L24 
.L72: 
     leaq 24(%rsp), %r13 
     leaq 32(%rsp), %rdi 
     xorl %edx, %edx 
     movq %r13, %rsi 
     call std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::_M_create(unsigned long&, unsigned long) 
     movq 24(%rsp), %rdx 
     movq %rax, 32(%rsp) 
     movq %rax, %rdi 
     movq %rdx, 48(%rsp) 
.L28: 
     movq %rbp, %rdx 
     movq %r15, %rsi 
     call memcpy 
     movq 24(%rsp), %rax 
     movq 32(%rsp), %rdx 
     jmp  .L30 
.L70: 
     movzbl (%r15), %eax 
     movl $1, %edx 
     movb %al, 80(%rsp) 
     leaq 80(%rsp), %rax 
     jmp  .L24 
.L73: 
     movzbl (%r15), %eax 
     leaq 48(%rsp), %rdx 
     leaq 24(%rsp), %r13 
     movb %al, 48(%rsp) 
     movl $1, %eax 
     jmp  .L30 
     movq %rax, %rbp 
     jmp  .L36 
     movq %rax, %rbp 
     jmp  .L39 
.L74: 
     leaq 48(%rsp), %rdi 
     leaq 24(%rsp), %r13 
     jmp  .L28 
.L71: 
     movq %rax, %rdi 
     jmp  .L22 
.L66: 
     movq %rax, %rbp 
     movq 32(%rsp), %rdi 
     leaq 48(%rsp), %rax 
     cmpq %rax, %rdi 
     je  .L39 
     call operator delete(void*) 
.L39: 
     movq 64(%rsp), %rdi 
     leaq 80(%rsp), %rax 
     cmpq %rax, %rdi 
     je  .L36 
     call operator delete(void*) 
.L36: 
     movq 16(%rbx), %rdi 
     cmpq %rdi, %r14 
     je  .L41 
     call operator delete(void*) 
.L41: 
     movq %rbp, %rdi 
     call _Unwind_Resume 
     jmp  .L66 
foo2::foo2(int, double, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >): 
     pushq %r12 
     pushq %rbp 
     leaq 32(%rdi), %rbp 
     pushq %rbx 
     leaq 16(%rdx), %rax 
     movq %rdi, %rbx 
     subq $64, %rsp 
     movl %esi, (%rdi) 
     movq %rbp, 16(%rdi) 
     movq (%rdx), %rsi 
     movsd %xmm0, 8(%rdi) 
     cmpq %rax, %rsi 
     je  .L90 
     movq %rsi, 16(%rdi) 
     movq 16(%rdx), %rsi 
     movq %rsi, 32(%rdi) 
.L77: 
     movq 8(%rdx), %rsi 
     movq %rsi, 24(%rbx) 
     movq %rax, (%rdx) 
     leaq 48(%rsp), %rax 
     movq $0, 8(%rdx) 
     movb $0, 16(%rdx) 
     movq (%r8), %rdx 
     movq %rax, 32(%rsp) 
     leaq 16(%r8), %rax 
     cmpq %rax, %rdx 
     je  .L91 
     movq %rdx, 32(%rsp) 
     movq 16(%r8), %rdx 
     movq %rdx, 48(%rsp) 
.L79: 
     movq 8(%r8), %rdx 
     movq %rax, (%r8) 
     leaq 16(%rsp), %rax 
     movq $0, 8(%r8) 
     movb $0, 16(%r8) 
     movq %rax, (%rsp) 
     leaq 16(%rcx), %rax 
     movq %rdx, 40(%rsp) 
     movq (%rcx), %rdx 
     cmpq %rdx, %rax 
     je  .L92 
     movq %rdx, (%rsp) 
     movq 16(%rcx), %rdx 
     movq %rdx, 16(%rsp) 
.L81: 
     movq 8(%rcx), %rdx 
     leaq 48(%rbx), %rdi 
     movq %rax, (%rcx) 
     movq $0, 8(%rcx) 
     movb $0, 16(%rcx) 
     movq %rsp, %rsi 
     movq %rdx, 8(%rsp) 
     leaq 32(%rsp), %rdx 
     call bar::bar(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >) 
     movq (%rsp), %rdi 
     leaq 16(%rsp), %rax 
     cmpq %rax, %rdi 
     je  .L82 
     call operator delete(void*) 
.L82: 
     movq 32(%rsp), %rdi 
     leaq 48(%rsp), %rax 
     cmpq %rax, %rdi 
     je  .L75 
     call operator delete(void*) 
.L75: 
     addq $64, %rsp 
     popq %rbx 
     popq %rbp 
     popq %r12 
     ret 
.L90: 
     movq 16(%rdx), %rsi 
     movq 24(%rdx), %rdi 
     movq %rsi, 32(%rbx) 
     movq %rdi, 40(%rbx) 
     jmp  .L77 
.L91: 
     movq 16(%r8), %rsi 
     movq 24(%r8), %rdi 
     movq %rsi, 48(%rsp) 
     movq %rdi, 56(%rsp) 
     jmp  .L79 
.L92: 
     movq 16(%rcx), %rsi 
     movq 24(%rcx), %rdi 
     movq %rsi, 16(%rsp) 
     movq %rdi, 24(%rsp) 
     jmp  .L81 
     movq %rax, %r12 
     movq (%rsp), %rdi 
     leaq 16(%rsp), %rax 
     cmpq %rax, %rdi 
     je  .L85 
     call operator delete(void*) 
.L85: 
     movq 32(%rsp), %rdi 
     leaq 48(%rsp), %rax 
     cmpq %rax, %rdi 
     je  .L86 
     call operator delete(void*) 
.L86: 
     movq 16(%rbx), %rdi 
     cmpq %rdi, %rbp 
     je  .L87 
     call operator delete(void*) 
.L87: 
     movq %r12, %rdi 
     call _Unwind_Resume 
.LC4: 
     .string "bar" 
.LC5: 
     .string "foo" 
.LC6: 
     .string "hello" 
main: 
     pushq %rbx 
     movl $.LC4, %esi 
     subq $224, %rsp 
     leaq 160(%rsp), %rdi 
     call std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(char const*, std::allocator<char> const&) [clone .isra.16] 
     leaq 64(%rsp), %rdi 
     movl $.LC5, %esi 
     call std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(char const*, std::allocator<char> const&) [clone .isra.16] 
     leaq 32(%rsp), %rdi 
     movl $.LC6, %esi 
     call std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(char const*, std::allocator<char> const&) [clone .isra.16] 
     leaq 160(%rsp), %r8 
     leaq 64(%rsp), %rcx 
     leaq 32(%rsp), %rdx 
     movsd .LC7(%rip), %xmm0 
     leaq 96(%rsp), %rdi 
     movl $45, %esi 
     call foo::foo(int, double, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >) 
     movq 32(%rsp), %rdi 
     leaq 48(%rsp), %rax 
     cmpq %rax, %rdi 
     je  .L94 
     call operator delete(void*) 
.L94: 
     movq 64(%rsp), %rdi 
     leaq 80(%rsp), %rax 
     cmpq %rax, %rdi 
     je  .L95 
     call operator delete(void*) 
.L95: 
     movq 160(%rsp), %rdi 
     leaq 176(%rsp), %rax 
     cmpq %rax, %rdi 
     je  .L96 
     call operator delete(void*) 
.L96: 
     leaq 64(%rsp), %rdi 
     movl $.LC4, %esi 
     call std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(char const*, std::allocator<char> const&) [clone .isra.16] 
     leaq 32(%rsp), %rdi 
     movl $.LC5, %esi 
     call std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(char const*, std::allocator<char> const&) [clone .isra.16] 
     movl $.LC6, %esi 
     movq %rsp, %rdi 
     call std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(char const*, std::allocator<char> const&) [clone .isra.16] 
     leaq 64(%rsp), %r8 
     leaq 32(%rsp), %rcx 
     leaq 160(%rsp), %rdi 
     movsd .LC7(%rip), %xmm0 
     movq %rsp, %rdx 
     movl $45, %esi 
     call foo2::foo2(int, double, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >) 
     movq (%rsp), %rdi 
     leaq 16(%rsp), %rax 
     cmpq %rax, %rdi 
     je  .L97 
     call operator delete(void*) 
.L97: 
     movq 32(%rsp), %rdi 
     leaq 48(%rsp), %rax 
     cmpq %rax, %rdi 
     je  .L98 
     call operator delete(void*) 
.L98: 
     movq 64(%rsp), %rdi 
     leaq 80(%rsp), %rax 
     cmpq %rax, %rdi 
     je  .L99 
     call operator delete(void*) 
.L99: 
     movq 176(%rsp), %rdi 
     leaq 192(%rsp), %rax 
     cmpq %rax, %rdi 
     je  .L100 
     call operator delete(void*) 
.L100: 
     movq 112(%rsp), %rdi 
     leaq 128(%rsp), %rax 
     cmpq %rax, %rdi 
     je  .L123 
     call operator delete(void*) 
.L123: 
     addq $224, %rsp 
     xorl %eax, %eax 
     popq %rbx 
     ret 
     movq %rax, %rbx 
.L106: 
     movq 160(%rsp), %rdi 
     leaq 176(%rsp), %rdx 
     cmpq %rdx, %rdi 
     je  .L115 
.L125: 
     call operator delete(void*) 
.L115: 
     movq %rbx, %rdi 
     call _Unwind_Resume 
     movq (%rsp), %rdi 
     leaq 16(%rsp), %rdx 
     movq %rax, %rbx 
     cmpq %rdx, %rdi 
     je  .L110 
     call operator delete(void*) 
.L110: 
     movq 32(%rsp), %rdi 
     leaq 48(%rsp), %rdx 
     cmpq %rdx, %rdi 
     je  .L112 
     call operator delete(void*) 
.L112: 
     movq 64(%rsp), %rdi 
     leaq 80(%rsp), %rdx 
     cmpq %rdx, %rdi 
     je  .L114 
     call operator delete(void*) 
.L114: 
     movq 112(%rsp), %rdi 
     leaq 128(%rsp), %rdx 
     cmpq %rdx, %rdi 
     jne  .L125 
     jmp  .L115 
     movq %rax, %rbx 
     jmp  .L110 
     movq %rax, %rbx 
     jmp  .L112 
     movq %rax, %rbx 
     jmp  .L114 
     movq 32(%rsp), %rdi 
     leaq 48(%rsp), %rdx 
     movq %rax, %rbx 
     cmpq %rdx, %rdi 
     je  .L104 
     call operator delete(void*) 
.L104: 
     movq 64(%rsp), %rdi 
     leaq 80(%rsp), %rdx 
     cmpq %rdx, %rdi 
     je  .L106 
     call operator delete(void*) 
     jmp  .L106 
     movq %rax, %rbx 
     jmp  .L104 
.LC7: 
     .long 1717986918 
     .long 1076389478 
Смежные вопросы