создайте функцию to_vector, которая может условно приводить к элементам входного диапазона в зависимости от типа вывода

#c #templates #return-type

#c #шаблоны #возвращаемый тип

Вопрос:

Я хотел бы создать функцию to_vector, которая работает с входными данными vieweable_ranges. Я могу легко заставить это работать, если входной вид и выходной вектор имеют точно такой же тип, но не могу заставить его работать, если для вывода требуется постоянное приведение элементов входного диапазона. В моем случае входные диапазоны имеют неконстантные указатели, но на выходе могут указываться указатели const. Я хочу использовать его примерно так:

 auto a = to_vector( view ); // returns a vectorlt;obj *gt;. This case is easy vectorlt;const obj *gt; b = to_vector( view ); // returns a vector of const pointers. This gives error shown at the end  

Стандартная библиотека не имеет проблем с приведением указателей при создании вектора

 vectorlt;const obj *gt; c( view.begin(), view.end() );   

Но я не могу создать шаблонную функцию, чтобы она работала. Я перепробовал много вариантов, но я думаю, что моя ближайшая идея-что-то вроде этого:

 #include lt;rangesgt; #include lt;vectorgt;  using namespace std::ranges; template lt;range Range, typename T = range_value_tlt;Rangegt;gt; // default element type, T, based on input range inline std::vectorlt;Tgt; to_vector(Rangeamp;amp; r) { // idea: user specifies T. (doesn't work)  std::vectorlt;Tgt; v;   if constexpr (std::ranges::sized_rangelt;Tgt;) {  v.reserve(std::ranges::size(r));  }   std::copy(std::ranges::begin(r), std::ranges::end(r), std::back_inserter(v));  return v; }  

To_vector компилируется, но я получаю ошибку, когда пытаюсь запросить векторный вывод с ошибкой примерно такой:

не удается преобразовать std::векторlt;Obj *,std::распределительlt;Obj *gt;lt;Obj *gt;gt;’ в ‘std::векторlt;Obj *gt;gt;lt;const Obj *,std::распределительlt;const Obj *gt;lt;const Obj *gt;gt;

Ответ №1:

Как предложил Доминик Прайс, я разделил функцию шаблона на две отдельные функции. Первый работает со всеми объектами, но может иметь несколько более громоздкое соглашение о вызовах. Второй работает с указателями const, и там, где трудно получить тип std::представления.

 template lt;range R, typename T = range_value_tlt;Rgt; gt; inline auto to_vector(R amp;amp;r) {  return std::vectorlt;Tgt;(r.begin(), r.end()); // treats sized ranges correctly }  template lt;range R, typename Tgt; inline auto to_vector(R amp;amp;r, T amp;amp;) {  return std::vectorlt;Tgt;(r.begin(), r.end()); // treats sized ranges correctly }  

Это позволяет использовать следующий синтаксис от вызывающего абонента примерно так:

 struct Unit {}; using UnitPtr = Unit const *; using Units = vectorlt;UnitPtrgt;; unordered_setlt;Unit *gt; clusters; // clusters could even be a rvalue view  auto u = to_vector(clusters); // will return vectorlt;Unit *gt; Units v = to_vectorlt;Clusters, UnitPtrgt;(clusters); // need typenames...  Units w = to_vector(clusters, UnitPtr()); // ... or this for convertable  

Это может иметь тот недостаток, что аргумент T может быть сконструирован. Я использую только указатели, так что это не проблема.

Ответ №2:

Это связано с тем , что ваша функция создает возвращаемое значение типа std::vectorlt;Obj*gt; , которое действительно отличается от типа std::vectorlt;const Obj*gt; ; и поэтому, если std::vector не предоставлен перегруженный конструктор, который принял неконстантную версию самого себя, это преобразование невозможно. Возвращаемое значение выводится не по значению, которому вы присваиваете результат функции, а по параметру T шаблона, поэтому это будет работать, если вы явно вызовете функцию с T=const Obj*

Вам потребуется явно перегрузить функцию, чтобы построить вектор объектов const, либо передав параметр фиктивного тега, дополнительный параметр шаблона или to_const_vector функцию.

Комментарии:

1. std::вектор действительно предоставляет неконстантную версию самого себя. он также предоставляет тот, который принимает итераторы к неконстантным элементам и преобразует их в const. Но до сих пор я думаю, что вы правы в том, что, если я ограничу интерфейс to_vector тем, что я предоставил, нет никакого способа заставить компилятор вывести то, что я хочу. Я вижу, что это краткое описание языка и того, как он определен для обработки контейнеров const, элементов const и представлений const.

2. Что вы подразумеваете под «std::вектор предоставляет неконстантную версию самого себя»? Я предполагал, что преобразование будет возможно только с помощью конструктора с подписью vector(const vectorlt;std::remove_constlt;Tgt;::typegt;amp;)

3. Я понимаю. Я имел в виду, что для преобразования можно использовать итераторную версию конструкторов, как в примере в op.