<!-- TITLE: C++ Musings --> <!-- SUBTITLE: Incoherent ramblings about C++ --> # Returning by const reference for large objects is better than hoping for copy elision If `T` is used over `const T&`, a copy has to be generated, even if the caller is only interested in a reference. If `const T&` is used and the caller only wants a reference, no copy is needed. ```cpp struct LargeStruct { int values[20480]; }; struct Container { LargeStruct s; const LargeStruct& ref() const { return s; } LargeStruct val() const { return s; } }; int byValue(const Container& b, int i) { return b.val().values[i]; } int byRef(const Container& b, int i) { return b.ref().values[i]; } ``` GCC 6.3 with -O3 and Clang with -O3: ```assembly byValue(Container const&, int): push rbx mov edx, 81920 movsx rbx, esi mov rsi, rdi sub rsp, 81920 mov rdi, rsp call memcpy mov eax, DWORD PTR [rsp+rbx*4] add rsp, 81920 pop rbx ret byRef(Container const&, int): movsx rsi, esi mov eax, DWORD PTR [rdi+rsi*4] ret ``` # Defining textual constants as `const char*` or `constexpr const char*` is better than `std::string` `std::string` does not store the text locally, but has to execute a `new` and `delete` at construction time, which `const char*` and `constexpr const char*` do not have to do. ```cpp #include <string> namespace { std::string a = "asdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasd"; const char* b = "dgfkdgdgfkdgdgfkdgdgfkdgdgfkdgdgfkdgdgfkdgdgfkdgdgfkdgdgfkdg"; constexpr const char* c = "sdfsdfsdfsdfsdfsdfsdfsdfsdfsdfsdfsdfsdfsdfsdfsdfsdfsdfsdfsdf"; } const char* fooA() { return a.c_str(); } const char* fooB() { return b; } const char* fooc() { return c; } ``` Clang 5.0.0 with -std=c++17 -O3 ```assembly std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string(): # @std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string() mov rax, rdi mov rdi, qword ptr [rax] add rax, 16 cmp rdi, rax je .LBB0_1 jmp operator delete(void*) # TAILCALL .LBB0_1: ret fooA(): # @fooA() mov rax, qword ptr [rip + (anonymous namespace)::a[abi:cxx11]] ret fooB(): # @fooB() mov eax, .L.str.2 ret fooc(): # @fooc() mov eax, .L.str.1 ret _GLOBAL__sub_I_example.cpp: # @_GLOBAL__sub_I_example.cpp push rax mov qword ptr [rip + (anonymous namespace)::a[abi:cxx11]], (anonymous namespace)::a[abi:cxx11]+16 mov edi, 61 call operator new(unsigned long) mov qword ptr [rip + (anonymous namespace)::a[abi:cxx11]], rax mov qword ptr [rip + (anonymous namespace)::a[abi:cxx11]+16], 60 movups xmm0, xmmword ptr [rip + .L.str+44] movups xmmword ptr [rax + 44], xmm0 movups xmm0, xmmword ptr [rip + .L.str+32] movups xmmword ptr [rax + 32], xmm0 movups xmm0, xmmword ptr [rip + .L.str+16] movups xmmword ptr [rax + 16], xmm0 movups xmm0, xmmword ptr [rip + .L.str] movups xmmword ptr [rax], xmm0 mov qword ptr [rip + (anonymous namespace)::a[abi:cxx11]+8], 60 mov byte ptr [rax + 60], 0 mov edi, std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string() mov esi, (anonymous namespace)::a[abi:cxx11] mov edx, __dso_handle pop rax jmp __cxa_atexit # TAILCALL .L.str: .asciz "asdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasdasd" .L.str.1: .asciz "sdfsdfsdfsdfsdfsdfsdfsdfsdfsdfsdfsdfsdfsdfsdfsdfsdfsdfsdfsdf" .L.str.2: .asciz "dgfkdgdgfkdgdgfkdgdgfkdgdgfkdgdgfkdgdgfkdgdgfkdgdgfkdgdgfkdg" ``` GCC 7.3 with -std=c++17 -O3: ```assembly std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string(): mov rdx, QWORD PTR [rdi] lea rax, [rdi+16] cmp rdx, rax je .L1 mov rdi, rdx jmp operator delete(void*) .L1: rep ret fooA(): mov rax, QWORD PTR (anonymous namespace)::a[rip] ret .LC0: .string "dgfkdgdgfkdgdgfkdgdgfkdgdgfkdgdgfkdgdgfkdgdgfkdgdgfkdgdgfkdg" fooB(): mov eax, OFFSET FLAT:.LC0 ret .LC1: .string "sdfsdfsdfsdfsdfsdfsdfsdfsdfsdfsdfsdfsdfsdfsdfsdfsdfsdfsdfsdf" fooc(): mov eax, OFFSET FLAT:.LC1 ret _GLOBAL__sub_I__Z4fooAv: sub rsp, 8 mov edi, 61 mov QWORD PTR (anonymous namespace)::a[rip], OFFSET FLAT:(anonymous namespace)::a+16 call operator new(unsigned long) movdqa xmm0, XMMWORD PTR .LC2[rip] movabs rcx, 8314036833820636001 mov QWORD PTR (anonymous namespace)::a[rip], rax mov QWORD PTR (anonymous namespace)::a[rip+16], 60 mov edx, OFFSET FLAT:__dso_handle movups XMMWORD PTR [rax], xmm0 mov QWORD PTR [rax+48], rcx mov DWORD PTR [rax+56], 1685283172 mov esi, OFFSET FLAT:(anonymous namespace)::a mov edi, OFFSET FLAT:std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::~basic_string() movdqa xmm0, XMMWORD PTR .LC3[rip] mov QWORD PTR (anonymous namespace)::a[rip+8], 60 mov BYTE PTR [rax+60], 0 movups XMMWORD PTR [rax+16], xmm0 movdqa xmm0, XMMWORD PTR .LC4[rip] movups XMMWORD PTR [rax+32], xmm0 add rsp, 8 jmp __cxa_atexit .LC2: .quad 8314036833820636001 .quad 7017860981484380516 .LC3: .quad 7238236110174905459 .quad 8314036833820636001 .LC4: .quad 7017860981484380516 .quad 7238236110174905459 ```