СФИНАЦИЯ — любой контейнер в представлении массива в стиле c

Я делаю простой, не владеющий классом вид массива:

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

Я хочу построить его из любого контейнера, который имеет data() а также size() функции-члены, но SFINAE-d правильно, что array_view конструируемый только из некоторого контейнера C если это будет тогда действительным и безопасным поведением на самом деле пройти data_,

Я пошел с:

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())
{ }

Это кажется совершенно неудовлетворительным, и я даже не уверен, что это правильно. Правильно ли я включил все нужные контейнеры и исключил все неправильные? Есть ли более простой способ написать это требование?

2

Решение

Если мы посмотрим на предложенный std::experimental::array_view в N4512, мы находим следующее Viewable требование в таблице 104:

Выражение Тип возврата Операционная семантика

v.size () Конвертируется в ptrdiff_t

v.data () Тип T * такой, что T * является static_cast (v.data ()) указывает на
неявно преобразуемый в U *, непрерывная последовательность по крайней мере
и is_same_v<remove_cv_t<T>, v.size () объекты (возможно,
remove_cv_t<U>> правда. cv-квалифицированный) тип remove_cv_t<U>,

То есть авторы используют по существу одну и ту же проверку для .data(), но добавьте еще один для .size(),

Чтобы использовать арифметику указателя на U используя операции с Tтипы должны быть аналогичный согласно [expr.add] p6. сходство определяется для квалификационных преобразований, поэтому проверяется неявная конвертируемость, а затем проверяется сходство (через is_same) достаточно для арифметики указателей.

Конечно, нет никакой гарантии для операционной семантики.


В стандартной библиотеке единственные смежные контейнеры std::array а также std::vector, Есть также std::basic_string который имеет .data() член, но std::initializer_list нет, несмотря на то, что это смежно.

Все .data() Функции-члены указываются для каждого отдельного класса, но все они возвращают фактический указатель (без итератора, без прокси).

Это означает, что проверка на наличие .data() в настоящее время достаточно для контейнеров стандартной библиотеки; Вы хотите добавить проверку на конвертируемость, чтобы сделать array_view менее жадный (например, array_view<int> отвергая некоторые char* data()).


Реализация, конечно, может быть удалена от интерфейса; Вы можете использовать Концепции, эмуляцию концепций или просто enable_if с соответствующей функцией типа. Например.

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())
{ }

И да, это не соответствует обычной методике для функции типа, но она короче, и вы поняли идею.

3

Другие решения

Других решений пока нет …