c++ - How can I avoid std::vector<> to initialize all its elements? -
edit: edited both question , title more precise.
considering following source code:
#include <vector> struct xyz { xyz() { } // empty constructor, compiler doesn't care xyz(const xyz& o): v(o.v) { } xyz& operator=(const xyz& o) { v=o.v; return *this; } int v; // <will initialized int(), means 0 }; std::vector<xyz> test() { return std::vector<xyz>(1024); // memset() :-( }
...how can avoid memory allocated vector<> initialized copies of first element, o(n) operation i'd rather skip sake of speed, since default constructor nothing ?
a g++ specific solution do, if no generic 1 exists (but couldn't find attribute that).
edit: generated code follows (command line: arm-elf-g++-4.5 -o3 -s -fno-verbose-asm -o - test.cpp | arm-elf-c++filt | grep -ve '^[[:space:]]+[.@].*$' )
test(): mov r3, #0 stmfd sp!, {r4, lr} mov r4, r0 str r3, [r0, #0] str r3, [r0, #4] str r3, [r0, #8] mov r0, #4096 bl operator new(unsigned long) add r1, r0, #4096 add r2, r0, #4080 str r0, [r4, #0] stmib r4, {r0, r1} add r2, r2, #12 b .l4 @ .l8: @ add r0, r0, #4 @ .l4: @ cmp r0, #0 @ fill memory movne r3, #0 @ strne r3, [r0, #0] @ cmp r0, r2 @ bne .l8 @ str r1, [r4, #4] mov r0, r4 ldmfd sp!, {r4, pc}
edit: sake of completeness, here assembly x86_64:
.globl test() test(): lfb450: pushq %rbp lcfi0: movq %rsp, %rbp lcfi1: pushq %rbx lcfi2: movq %rdi, %rbx subq $8, %rsp lcfi3: movq $0, (%rdi) movq $0, 8(%rdi) movq $0, 16(%rdi) movl $4096, %edi call operator new(unsigned long) leaq 4096(%rax), %rcx movq %rax, (%rbx) movq %rax, 8(%rbx) leaq 4092(%rax), %rdx movq %rcx, 16(%rbx) jmp l4 @ l8: @ addq $4, %rax @ l4: @ testq %rax, %rax @ memory-filling loop je l2 @ movl $0, (%rax) @ l2: @ cmpq %rdx, %rax @ jne l8 @ movq %rcx, 8(%rbx) movq %rbx, %rax addq $8, %rsp popq %rbx leave lcfi4: ret lfe450: eh_frame1: lscie1: lecie1: lsfde1: lasfde1: lefde1:
edit: think conclusion not use std::vector<>
when want avoid unneeded initialization. ended unrolling own templated container, performs better (and has specialized versions neon , armv7).
the initialization of elements allocated controlled allocator template argument, if need customized, customize it. remember can wind-up in realm of dirty hacking, use caution. instance, here pretty dirty solution. avoid initialization, worse in performance, demonstration's sake (as people have said impossible!... impossible not in c++ programmer's vocabulary!):
template <typename t> class switch_init_allocator : public std::allocator< t > { private: bool* should_init; public: template <typename u> struct rebind { typedef switch_init_allocator<u> other; }; //provide required no-throw constructors / destructors: switch_init_allocator(bool* ashouldinit = null) throw() : std::allocator<t>(), should_init(ashouldinit) { }; switch_init_allocator(const switch_init_allocator<t>& rhs) throw() : std::allocator<t>(rhs), should_init(rhs.should_init) { }; template <typename u> switch_init_allocator(const switch_init_allocator<u>& rhs, bool* ashouldinit = null) throw() : std::allocator<t>(rhs), should_init(ashouldinit) { }; ~switch_init_allocator() throw() { }; //import required typedefs: typedef typename std::allocator<t>::value_type value_type; typedef typename std::allocator<t>::pointer pointer; typedef typename std::allocator<t>::reference reference; typedef typename std::allocator<t>::const_pointer const_pointer; typedef typename std::allocator<t>::const_reference const_reference; typedef typename std::allocator<t>::size_type size_type; typedef typename std::allocator<t>::difference_type difference_type; //redefine construct function (hiding base-class version): void construct( pointer p, const_reference cr) { if((should_init) && (*should_init)) new ((void*)p) t ( cr ); //else, nothing. }; }; template <typename t> class my_vector : public std::vector<t, switch_init_allocator<t> > { public: typedef std::vector<t, switch_init_allocator<t> > base_type; typedef switch_init_allocator<t> allocator_type; typedef std::vector<t, allocator_type > vector_type; typedef typename base_type::size_type size_type; private: bool switch_flag; //the order here important!! vector_type vec; public: my_vector(size_type acount) : switch_flag(false), vec(acount, allocator_type(&switch_flag)) { }; //... , rest of wrapper class... vector_type& get_vector() { return vec; }; const vector_type& get_vector() const { return vec; }; void set_switch(bool value) { switch_flag = value; }; }; class xyz{}; int main(){ my_vector<xyz> v(1024); //this won't initialize memory @ all. v.set_switch(true); //set true turn initialization on (needed resizing , such) }
of course, above awkward , not recommended, , won't better letting memory filled copies of first element (especially since use of flag-checking impede on each element-construction). avenue explore when looking optimize allocation , initialization of elements in stl container, wanted show it. point place can inject code stop std::vector container calling copy-constructor initialize elements in construct function of vector's allocator object.
also, away "switch" , "no-init-allocator", then, turn off copy-construction needed copy data during resizing (which make vector class less useful).
Comments
Post a Comment