Reputation: 9619
I am aware of the advantages an initializer list has over normal constructor body (default construction followed by assignment, instead of construction).
I am also aware of the optimization capabilities of modern compilers.
Are the modern compilers smart enough to optimize a non-initializer list constructor to the former type? If yes, is it restricted to just the basic types, or are user-defined types included as well? If not, why not?
Upvotes: 1
Views: 205
Reputation: 69864
Here's how gcc5.3 handles it with -O2. Your suspicions are correct - in trivial cases the optimiser makes up for sloppy programming.
The problem occurs when the compiler can't see into the constructor or assignment operator of member variables (in this case, because they are defined in another translation unit).
When that happens, you get better code (at least with GCC and I suspect with all others) if the constructors are written properly:
test code:
#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");
}
example assembler output:
.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
Upvotes: 1