Sunday 15 September 2013

C++/C++11 Efficient way to have static array/vector of objects initialized with initializer list, and supporting range-based for -



C++/C++11 Efficient way to have static array/vector of objects initialized with initializer list, and supporting range-based for -

suppose want have static array of pre-defined values/objects (const or non-const) associated class. possible options utilize std:vector, std::array or c-style array (ie. []), or . example,

in .hpp:

class myclass { public: static const std::vector<myclass> vec_pre; // no efficient way build initializer list, since uses re-create contructor, when using std::move static const std::array<myclass, 2> arr_pre; // have specify size inconvenient static const myclass carr_pre[]; // not compatible c++11 for-range since size undefined };

in .cpp

const std::vector<myclass> myclass::vec_pre = { std::move(myclass{1,2,3}), std::move(myclass{4,5,6}) }; // note: still uses re-create constructor const std::array<myclass, 2> myclass::arr_pre= { myclass{1,2,3}, myclass{4,5,6} }; const zwscolour zwscolour::carr_pre[] = { myclass{1,2,3}, myclass{1,2,3} }

when writing intially, chose std::vector since don't have specify size, goodness of vector class, , seems modern c++ way it. problem: while testing, noticed phone call move contructor, still phone call re-create constructor each element. reason std::initializer_list allows const access members, , vector has re-create them initializer_list own storage. though it's done 1 time @ startup, this inefficient , there doesn't appear way around it, looked @ other options (std::array , c-array[]).

second selection utilize std::array, modern c++ way, , doesn't suffer problem of calling re-create constructor each value since doesn't need create copies (not sure why though ?). std::array has benefit don't need wrap each value in std::move(). however, has annoyance have specify size first, every time add/remove elements, have alter size well. there ways around this none of them ideal. @ricky65 states, should able do

std::array <int> arr = { 1, 3, 3, 7, 0, 4, 2, 0, 3, 1, 4, 1, 5, 9 }; //automatically deduces size initializer list :)

this leaves me lastly alternative - old c-style array [] - has benefits don't have specify size, , efficient in doesn't phone call re-create constructor each object. downsides it's not modern c++, , biggest downside if don't specify size of array in .hpp header, c++11 for-range doesn't work compiler complains

cannot utilize incomplete type 'const myclass []' range

you can overcome error either specifying size of array in header (but inconvenient , produces hard-to-maintain code, need adjust size every time add/remove items initializer list), or alternatively utilize constexpr , declare array , values in .hpp header

constexpr static myarray my_array[] = { myclass{1,2,3}, myclass{4,5,6} };

note: constexpr "work-around" work pod's , cannot used in case class objects. above illustration result in compile-time error invalid utilize of incomplete type 'myclass'

i'm trying write best-practice modern c++ possible (eg. using copy-and-swap idiom), , wonder best way define static arrays class...

without having specify size which don't need re-create constructed (or move constructed either, if possible) which can used c++ for-range which don't need specified in header file should compile/work clang/llvm 3.5, visual studio 2013 update 4 rc, , gcc 4.8.1.

edit1: another post vector problem of not beingness able move values initializer list

edit2: more info on using std::array without need specify size, creates/uses make_array(), , mentions there proposal make_array() become a standard. original link courtesy of comment @neil kirk.

edit3: problem vector method (at to the lowest degree in case) cannot iterate on items using const t or t. allows iteration using const t& (when it's static const) , const t&/t& (when it's static). what's reason limitation ?

descriptive reply solutions

@yakk's solution appears solution, , works on visual c++ 2013 update 4 rc.

i find staggering such trivial issue hard implement using latest c++11/14 standard.

the info not have stored within class. in fact, storing info within static fellow member of class leaking implementation details.

all need expose info available, , info global class type. not involve exposing storage details: need expose storage access details.

in particular, want expose ability for(:) loop on data, , operate on in c++11 style way. expose that.

store info in anonymous namespace in class's .cpp file in c-style array (or std::array, don't care).

expose in class following:

namespace details { template< class r, class iterator_traits, class iterator_category, bool is_random_access=std::is_base_of< std::random_access_iterator_tag, iterator_category >::value > struct random_access_support {}; template<class r, class iterator_traits, class iterator_category> struct random_access_support<r, iterator_traits, iterator_category, true> { r const* self() const { homecoming static_cast<r const*>(this); } template<class s> typename iterator_traits::reference operator[](s&&s) const { homecoming self()->begin()[std::forward<s>(s)]; } std::size_t size() const { homecoming self()->end()-self()->begin(); } }; } template<class it> struct range:details::random_access_support< range<it>, std::iterator_traits<it>, typename std::iterator_traits<it>::iterator_category > { using value_type = typename std::iterator_traits<it>::value_type; using reference = typename std::iterator_traits<it>::reference; using iterator = it; using iterator_category = typename std::iterator_traits<it>::iterator_category; using pointer = typename std::iterator_traits<it>::pointer; begin() const { homecoming b; } end() const { homecoming e; } bool empty() const { homecoming b==e; } reference front() const { homecoming *b; } reference back() const { homecoming *std::prev(e); } range( s, f ):b(s),e(f) {} range()=default; range(range const&)=default; range& operator=(range const&)=default; private: b; e; }; namespace details { template<class t> struct array_view_helper:range<t*> { using non_const_t = typename std::remove_const<t>::type; t* data() const { homecoming this->begin(); } array_view_helper( array_view_helper const& ) = default; array_view_helper():range<t*>(nullptr, nullptr){} array_view_helper& operator=(array_view_helper const&)=default; template<class a> explicit operator std::vector<non_const_t, a>() const { homecoming { this->begin(), this->end() }; } std::vector<non_const_t> as_vector() const { homecoming std::vector<non_const_t>(*this); } template<std::size_t n> array_view_helper( t(&arr)[n] ):range<t*>(arr+0, arr+n) {} template<std::size_t n> array_view_helper( std::array<t,n>&arr ):range<t*>(arr.data(), arr.data()+n) {} template<class a> array_view_helper( std::vector<t,a>&vec ):range<t*>(vec.data(), vec.data()+vec.size()) {} array_view_helper( t*s, t*f ):range<t*>(s,f) {} }; } // non-const template<class t> struct array_view:details::array_view_helper<t> { using base of operations = details::array_view_helper<t>; // using base::base in c++11 compliant compilers: template<std::size_t n> array_view( t(&arr)[n] ):base(arr) {} template<std::size_t n> array_view( std::array<t,n>&arr ):base(arr) {} template<class a> array_view( std::vector<t,a>&vec ):base(vec) {} array_view( t*s, t*f ):base(s,f) {} // special methods: array_view( array_view const& ) = default; array_view() = default; array_view& operator=(array_view const&)=default; }; template<class t> struct array_view<t const>:details::array_view_helper<const t> { using base of operations = details::array_view_helper<const t>; // using base::base in c++11 compliant compilers: template<std::size_t n> array_view( std::array<t const,n>&arr ):base(arr) {} array_view( t const*s, t const*f ):base(s,f) {} template<std::size_t n> array_view( t const(&arr)[n] ):base(arr) {} // special methods: array_view( array_view const& ) = default; array_view() = default; array_view& operator=(array_view const&)=default; // const t constructors: template<std::size_t n> array_view( std::array<t,n> const&arr ):base(arr.data(), arr.data()+n) {} template<std::size_t n> array_view( std::array<t const,n> const&arr ):base(arr.data(), arr.data()+n) {} template<class a> array_view( std::vector<t,a> const&vec ):base(vec.data(), vec.data()+vec.size()) {} array_view( std::initializer_list<t> il):base(il.begin(), il.end()) {} };

which @ to the lowest degree sketch of view classes. live example

then expose array_view<myclass> static fellow member of class, initialized array created in .cpp file.

range<it> range of iterators acts non-owning container. tomfoolery done block non-constant-time calls size or [] @ sfinae level. back() exposed , fails compile if phone call on invalid iterators.

a make_range(container) makes range<it> more useful.

array_view<t> range<t*> has bunch of constructors contiguous buffer containers, c-arrays, std::arrays , std::vectors. (actually exhaustive list).

this useful because access through array_view efficient access raw pointer-to-first-element of array, many of nice methods containers have, , works range-for loops. in general, if function takes std::vector<t> const& v, can replace function takes array_view<t> v , drop-in replacement. big exception operator vector, explicit, avoid accidental allocations.

c++ c++11 for-loop static

No comments:

Post a Comment