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 ?
@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::array
s , std::vector
s. (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