проверить, можно ли перемещать элементы диапазона? - PullRequest
2 голосов
/ 12 мая 2019

Я пробую интерфейс диапазона c ++ 20, и я добавил конструктор, который принимает диапазон в моем контейнероподобном типе.

class element {
  ...
};

class myclass {
public:
  template <typename Iter>
  myclass(Iter first, Iter last)
    : els(first, last)
  { }

  template <typename Range>
  myclass(Range&& r);

private:
  std::vector<element> els;
};

Версия с парой итераторов довольно проста. В els_(first, last); он копирует элементы, если Iter - обычный итератор, и перемещает элементы, если Iter - подвижный итератор, такой как std::move_iterator<>. Ответственность за передачу перемещаемых итераторов лежит на вызывающей стороне, если вызывающая сторона хочет, чтобы элементы были перемещены.

Однако в версии диапазона, хотя я могу проверить, указан ли сам диапазон в ссылке rvalue или ссылке lvalue, это не помогает проверить, может ли элемент быть перемещен или нет.

Допустим, у нас есть создатель диапазона, make_range(), который берет контейнер и возвращает экземпляр прокси, который соответствует концепции диапазона. В следующем коде Range конструктора является rvalue-ссылкой в ​​обоих случаях, но очевидно, что элементы не должны перемещаться во втором случае.

std::list<element> list_of_elements{...};
myclass c(std::move(list_of_elements)); // should be moved

std::list<element> list_of_elements_to_be_reused{...};
myclass c(make_range(list_of_elements_to_be_reused)); // should not be moved

Как я могу проверить, предназначен ли данный диапазон для копирования для перемещения?

1 Ответ

2 голосов
/ 12 мая 2019

Ты не.Вы доверяете итератору, возвращенному std::ranges::begin, так же, как вы доверяете итератору Iter, чтобы он поступал правильно.Ваш второй конструктор может просто делегировать:

template <std::ranges::Range Range> // Constraints checked with the library concept
myclass(Range&& r) : myclass(std::ranges::begin(std::move(r)), std::ranges::end(std::move(r)))
{}

Поведение по умолчанию - для копирования, что разумно.Но поскольку std::ranges::begin является объектом точки настройки, он может обрабатывать определенные пользователем перегрузки begin с помощью ADL.Для пользовательского типа эта настройка:

namespace myns {
    class myclass { /* ... */ };
    auto begin(std::vector<myclass>&& v) { return std::make_move_iterator(v.begin()); }
    auto end(std::vector<myclass>&& v) { /* ... * }
}

Будет std::ranges::begin вызывать myns::begin, когда конструктору класса будет передан вектор-значение.Это пользовательский тип, который управляет поведением, что хорошо.

...