c++ - SFINAE-ing any container into a c-style array view -


i'm making simple, non-owning array view class:

template <typename t> class array_view {     t* data_;     size_t len_;     // ... }; 

i want construct container has data() , size() member functions, sfinae-d correctly such array_view constructible container c if valid , safe behavior traverse data_.

i went with:

template <typename c,           typename d = decltype(std::declval<c>().data()),           typename = std::enable_if_t<               std::is_convertible<d, t*>::value &&               std::is_same<std::remove_cv_t<t>,                             std::remove_cv_t<std::remove_pointer_t<d>>>::value>            > array_view(c&& container) : data_(container.data()), len_(container.size()) { } 

that seems wholly unsatisfying , i'm not sure it's correct. correctly including right containers , excluding wrong ones? there easier way write requirement?

if take @ proposed std::experimental::array_view in n4512, find following viewable requirement in table 104:

 expression   return type                    operational semantics  v.size()    convertible ptrdiff_t  v.data()    type t* such t*        static_cast(v.data()) points             implicitly convertible u*,  contiguous sequence of @ least             , is_same_v<remove_cv_t<t>,  v.size() objects of (possibly             remove_cv_t<u>> true.       cv-qualified) type remove_cv_t<u>. 

that is, authors using same check .data(), add 1 .size().

in order use pointer arithmetic on u using operations t, types need similar according [expr.add]p6. similarity defined qualification conversions, why checking implicit convertibility , checking similarity (via is_same) sufficient pointer arithmetic.

of course, there's no guarantee operational semantics.


in standard library, contiguous containers std::array , std::vector. there's std::basic_string has .data() member, std::initializer_list not, despite being contiguous.

all of .data() member functions specified each individual class, return actual pointer (no iterator, no proxy).

this means checking existence of .data() sufficient standard library containers; you'd want add check convertibility make array_view less greedy (e.g. array_view<int> rejecting char* data()).


the implementation can of course moved away interface; use concepts, concepts emulation, or enable_if appropriate type function. e.g.

template<typename t, typename as,          typename size_rt = decltype(std::declval<t>().size())          typename data_rt = decltype(std::declval<t>().data())> constexpr bool is_viewable =     std::is_convertible_v<size_rt, std::ptrdiff_t>     && std::is_convertible_v<data_rt, t*>     && std::is_same_v<std::remove_cv_t<t>, std::remove_cv_t<data_rt>>;  template <typename c,           typename = std::enable_if_t<is_viewable<c, t>>          > array_view(c&& container)     : data_(container.data()), len_(container.size()) { } 

and yes, doesn't follow usual technique type function, shorter , idea.


Comments