Я пишу анализатор файлов. Поскольку пользователь может пройти через файл только один раз, я хочу, чтобы итератор был доступен только из ссылки rvalue.
MyParser parser("/path/to/file");
for(auto it : std::move(parser)){
// example
for(auto & [key, value]: it){
std::court << key << '\t' << value << std::endl;
}
}
Итак, я сделал следующее
class MyParser{
public:
MaParser(const std::string &path): begin_iterator_(path){}
// ====== Begin iterator class ==============
class iterator{
public:
typedef std::map<std::string, std::string> value_type;
// Ctor
iterator(const std::string & path):{
// Initialize
}
// Default ctor
iterator() = default;
// Move ctor
iterator(iterator && other){
istream_ = std::move(other.istream_);
values_ = std::move(other.value_);
}
iterator & operator=(iterator && other){
istream_ = std::move(other.istream_);
values_ = std::move(other.value_);
return *this;
}
void operator++(){// definition}
value_type & operator*(){
return value_;
}
bool operator==(const iterator & other) const {
return false; // logic here
}
bool operator!=(const iterator & other) const {
return !(*this == other);
}
private:
std::unique_ptr<std::istream> istream_;
value_type value_;
};
// ====== End iterator class ==============
iterator && begin() &&{
return std::move(begin_iterator_);
}
iterator && end() && {
return iterator();
}
private:
iterator begin_iterator_;
};
Теперь я могу повторять так:
for(auto it = std::move(parser).begin(); it != myParser::iterator(); ++it){
}
но когда я пытаюсь повторить в соответствии с первоначальным намерением, я получаю сообщение об ошибке
cannot convert 'this' pointer from 'MyParser' to 'MyParser &&'
.
Q1: Как решить эту проблему?
Q2: Если вместо begin_iterator в функции begin сделать
iterator && begin() &&{
return iterator(path_); // Assuming we stored the path in a local variable.
}
программа вылетает на итераторе init auto it = std::move(parser).begin();
при попытке переместить значение_ в конструкторе перемещения. Почему?
Вы можете поиграть с категорией итератора через std::iterator_traits.
Основанный на диапазоне for - это в основном синтаксический сахар. Ваша петля:
for (auto it : std::move(parser)) {
statements;
}
эквивалентно:
{
auto && __range = std::move(parser);
for (auto __begin = __range.begin(), end = __range.end(); __begin != __end; ++__begin) {
auto it = *__begin;
{
statements;
}
}
}
Таким образом, хотя __range
является ссылкой на rvalue, __range.begin()
по-прежнему вызывает begin
на lvalue (rvalue-ness не используется).
Обойти это невозможно, вы не можете сделать так, чтобы в диапазоне for можно было повторять только rvalue. Для справки, std::ranges::istream_view делает то же самое, и единственное, что он делает для безопасности, — делает свой итератор доступным только для перемещения, поэтому того, что у вас уже есть, должно быть достаточно.
Что касается вашего второго вопроса, если вы вернете временное значение из iterator && begin() &&
, оно будет уничтожено до того, как функция вернется, и вы получите висячую ссылку. Ваша текущая функция end
страдает от той же проблемы. Просто верните значение iterator begin() &&
. Переход от временного следует исключить.
[OT]: в "
for(auto it : std::move(parser))
" имяit
вводит в заблуждение, это не итератор (похоже, это пара, поэтомуp
было бы лучше)