Есть некий итератор стандартного контейнера, а из него надо получить указатель на объект этого контейнера.
Многие советуют использовать конструкцию вида: ptr = &*it;
но для past-the-end итератора такая конструкция не валидна.
Вроде бы для past-the-end итератора можно использовать конструкцию вида: ptr = it.operator->();
но у меня возникли сомнения...
Поэтому вопросы следующие:
Есть ли гарантия, что it.operator->() вернёт именно указатель, а не некий тип его имитирующий?
Верно ли, что если it — это past-the-end итератор, то результат it.operator->() — это past-the-end указатель? Или это зависит от типа контейнера?
Верно ли, что для пустого контейнера x, следующий код — это UB:
Здравствуйте, B0FEE664, Вы писали:
BFE>Есть некий итератор стандартного контейнера, а из него надо получить указатель на объект этого контейнера. BFE>Многие советуют использовать конструкцию вида: BFE>ptr = &*it;
Вообще-то, std::addressof(*it). Так как operator& не обязан возвращать указатель, а даже если и возвращает, то не обязан возвращать его для элемента *it.
Только даже это не всегда имеет смысл: попробуй в пресловутом std::vector<bool> адрес элемента взять.
BFE>но для past-the-end итератора такая конструкция не валидна.
Верно.
BFE>Вроде бы для past-the-end итератора можно использовать конструкцию вида: BFE>ptr = it.operator->();
Нельзя
BFE>Есть ли гарантия, что it.operator->() вернёт именно указатель, а не некий тип его имитирующий?
Нет.
В C++ специально сделали для opeator-> особые правила, для этого случая, чтобы можно было возвращать не указатели из opeator->, а компилятор потом их сам в цепочку вызовов выстраивал.
BFE>Верно ли, что для пустого контейнера x, следующий код — это UB:
Для любого контейнера это UB.
Даже для std::basic_string формально не разрешено, хотя при этом str[str.size()] содержит нулевой символ, который можно читать.
Здравствуйте, watchmaker, Вы писали:
BFE>>Вроде бы для past-the-end итератора можно использовать конструкцию вида: BFE>>ptr = it.operator->(); W>Нельзя
Интересно, зачем так сделано? Ведь здесь нет разыменования.
W>Вообще-то, std::addressof(*it). Так как operator& не обязан возвращать указатель, а даже если и возвращает, то не обязан возвращать его для элемента *it. W>Только даже это не всегда имеет смысл: попробуй в пресловутом std::vector<bool> адрес элемента взять.
Так как итераторы — это обобщённые абстрактные указатели, то я предлагаю добавить каждому итератору метод get() который возвращает указатель на его объект или past-the-end указатель для past-the-end итератора или nullptr для всех случаев, когда указатель не может быть получен.
Возражения?
Здравствуйте, B0FEE664, Вы писали:
BFE>Есть некий итератор стандартного контейнера, а из него надо получить указатель на объект этого контейнера. BFE>Многие советуют использовать конструкцию вида: BFE>ptr = &*it; BFE>но для past-the-end итератора такая конструкция не валидна.
BFE>Вроде бы для past-the-end итератора можно использовать конструкцию вида: BFE>ptr = it.operator->(); BFE>но у меня возникли сомнения... BFE>Поэтому вопросы следующие: BFE>Есть ли гарантия, что it.operator->() вернёт именно указатель, а не некий тип его имитирующий? BFE>Верно ли, что если it — это past-the-end итератор, то результат it.operator->() — это past-the-end указатель? Или это зависит от типа контейнера?
BFE>Верно ли, что для пустого контейнера x, следующий код — это UB: BFE>
BFE>? Или же есть гарантия, что для такого случая значения указателей устанавливаются в NULL и всё ok?
1. Сперва надо проверять итератор на past-the-end.
2. У итератора может и не быть оператора стрелка.
3. Оператор * у итератора с iterator_category output_iterator_tag может возвращать ссылку. А может и прокси объект с оператором присвоения, как у vector<bool> ;
Следовательно взятие адреса после разыменования не всегда корректно. &*it не всегда можно делать.
Здравствуйте, B0FEE664, Вы писали:
BFE>Многие советуют использовать конструкцию вида: BFE>ptr = &*it; BFE>но для past-the-end итератора такая конструкция не валидна.
BFE>Вроде бы для past-the-end итератора можно использовать конструкцию вида: BFE>ptr = it.operator->();
То есть, использовать it.operator*() нельзя, а it.operator->() можно? Как такое может быть?
--
Не можешь достичь желаемого — пожелай достигнутого.
Здравствуйте, rg45, Вы писали:
R>То есть, использовать it.operator*() нельзя, а it.operator->() можно? Как такое может быть?
В простом случае, когда iterator::pointer — это указатель, а iterator::reference — это ссылка,
вызов it.operator*() приводит к разыменованию постпоследнего элемента, а it.operator->() возвращает указатель на память за последним элементом без разыменования этого указателя.
Указатель на постпоследний элемент можно получать и использовать для сравнения с другими указателями этого массива (специальная гарантия в стандарте).
Т.е. для не пустых вектора (не bool), для array или для строки, в теории, тут я не вижу никаких проблем, а на практике там assert'ы стоят.
Здравствуйте, B0FEE664, Вы писали:
BFE>Здравствуйте, rg45, Вы писали:
R>>То есть, использовать it.operator*() нельзя, а it.operator->() можно? Как такое может быть? BFE>В простом случае, когда iterator::pointer — это указатель, а iterator::reference — это ссылка,
В простом случае it->x равносильно (*it).x; Вот мне и непонятно, как может быть разрешено первое, если не разрешено второе.
--
Не можешь достичь желаемого — пожелай достигнутого.
Здравствуйте, rg45, Вы писали:
R>В простом случае it->x равносильно (*it).x; Вот мне и непонятно, как может быть разрешено первое, если не разрешено второе.
А причём тут x? Речь не про x, а исключительно про адрес его.
Получение x и получение адреса x — это две разные задачи.
Здравствуйте, B0FEE664, Вы писали:
R>>В простом случае it->x равносильно (*it).x; Вот мне и непонятно, как может быть разрешено первое, если не разрешено второе. BFE>А причём тут x? Речь не про x, а исключительно про адрес его.
А что ты называешь "простым" случаем тогда? И как в этом самом случае можно обойтись без x? Как должно выглядеть валидное выражение для "простого" случая?
--
Не можешь достичь желаемого — пожелай достигнутого.
Здравствуйте, B0FEE664, Вы писали:
BFE>Так как итераторы — это обобщённые абстрактные указатели, то я предлагаю добавить каждому итератору метод get() который возвращает указатель на его объект или past-the-end указатель для past-the-end итератора или nullptr для всех случаев, когда указатель не может быть получен. BFE>Возражения?
Ни разу не указатели.
Вот есть у меня итератор по установленным битам ( грубо -- набор значений enum отображается в целое как побитовое OR для (1<<enumValueA).
у него operator* возвращает текущий enum, а operator-> вообще не предусмотрен.
Здравствуйте, B0FEE664, Вы писали:
BFE>Получение x и получение адреса x — это две разные задачи.
Разные, но их технически нельзя различить учитывая семантику operator-> в его операторной форме.
Т.е. если бы можно было как-то перегрузить operator-> отдельно для случая, когда это просто возврат адреса, и отдельно для случая,
когда он участвует в выражении it->x, то тогда да, можно было бы ждать, что в первом случае ассерта не будет.
Здравствуйте, B0FEE664, Вы писали:
BFE>Есть некий итератор стандартного контейнера, а из него надо получить указатель на объект этого контейнера.
Итератор — не указатель. Какой-нибудь back_insert_iterator никогда не указывает ни на что валидное. Его разыменование позволяет втыкать нечто в конец.
BFE>Есть ли гарантия, что it.operator->() вернёт именно указатель, а не некий тип его имитирующий?