cpp-ru / ideas

Идеи по улучшению языка C++ для обсуждения
https://cpp-ru.github.io/proposals
Creative Commons Zero v1.0 Universal
90 stars 0 forks source link

Добавить постфиксый оператор разыменовывания #243

Open apolukhin opened 3 years ago

apolukhin commented 3 years ago

Перенос предложения: голоса +0, -12 Автор идеи: kitmouse

Попадаются длинные строки, возвращающие значение, которое надо разыменовать, чтобы сравнить, присвоить и т.д.. foobaar.foobarmember[longIndexVariableName]->retrievePointerToSomething()->vectorOfPointers[anotherIndex] = anotherThing; Выражение легко читается слева направо - до того момента, когда вам надо понять, произошло ли присвоение к указателю, хранящемуся в vectorOfPointers, или к тому, на что он указывает. Тогда нужно перевести взгляд на начало и проверить наличие

Похожий механизм в c++ уже есть - мы можем использовать -> чтобы разыменовать объект и сразу же обратиться к его члену вместо "(" слева от выражения и ")." справа от него, спасая разработчика от необходимости постоянно переводить взгляд влево-вправо. То что им пользуются показывает, что этот оператор добавлен в язык не зря. Но если вы хотите не обратиться к члену того, на что, на что указывает длинное выражение, а, например сравнить (==), присвоить(=) или проиндексировать( [] ) его? Для таких операций сокращений нет, придется использовать пару "(" и ")[idx]". А читая это, придется переводить взгляд туда-сюда, и следить за тем, какой "(*" относится к какому ")[idx]". Много сложнее. Именно для избежания подобного некогда был придуман ->

Возьмем строку из "основной мысли"

*foo.fooMember[IndexVariableName]->retrievePointer()->containerOfPointers[anotherIndex] = anotherThing;

Она уже плоха, из-за того, что чтобы прочесть ее надо из = перевести взгляд влево и проверить наличие * в начале, чтобы понять, что именно происходит. Теперь представьте себе, что containerOfPointers - это unique_ptr на абтрактный базовый класс, так что иначе чем через указатель им владеть нельзя:

*(*foo.fooMember[IndexVariableName]->retrievePointer()->containerOfPointers)[anotherIndex] = anotherThing;

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

*(*(*(*foo->anotherfoos)["name4"]->anotherfoos)[fooId]->getContainerSharedPtr)[0] != *bar

Сравните с версие

Можно продолжать приводить все более запутанные примеры, но я думаю, все и так уже поняли, что синтаксис получается запутанный. Его сложно читать, в нем проще простого пропустить ошибку и он попросту неприятен.

Ожним из обходов является "longExpression[0]", но работает оно только с сырыми указателями, и вводит читателя в заблуждение, т.к. в изначальном смысле означает "получить первый элемент контейнера" а не "разыменовать то, что слева".

Поэтому, я предлагаю добавить синтаксис постфиксного разыменовывания. Придумывать видоизмененную версию для операторов индексирования, присвоения и каждого оператора сравления было бы неразумно, поэтому остается некий постфиксный оператор. Какой именно оператор - не предмет этого предложения, но, для примера возьмем "<*".

Попробуем переписать примеры выше с использованием <*: (я не настаиваю на нем, это только как пример)

foo.fooMember[IndexVariableName]->retrievePointer()->containerOfPointers*<[anotherIndex]<* = anotherThing;

foo->anotherfoos<*["name4"]->anotherfoos<*[fooId]->getContainerSharedPtr<*[0]<* != *bar

красота оператора , но теперь можно читать слево направо. Теперь

apolukhin commented 3 years ago

Antervis, 28 ноября 2017, 14:31

ptr->operator[]("name")

Andrey Davydov, 28 ноября 2017, 15:41 Использовать для этих целей "<" самая неудачная идея из возможных, так как он уже конфликтует с шаблонными аргументами. Кажется, что C++ уже достиг такой сложности, что впихнуть в него еще один оператор не получится.

Antervis, 29 ноября 2017, 7:42 Andrey Davydov, можно было бы реализовать некоторые операторы в формате: p->[5], p->(), p->++, p->*= 5, вроде как никаких конфликтов не будет. Другой вопрос: нужны ли эти операторы? - код с их использованием будет тяжеловато читать.

На мой взгляд, если есть разыменования второго и более уровней, то проблема в архитектуре кода

Andrey Davydov, 29 ноября 2017, 10:03 Antervis, оператор ->* уже существует для разыменования member pointer'а, остальные операторы, вроде бы, возможны. В целом же, мне кажется, примеры автора предложения разумны, я не вижу в них архитектурных проблем, но читаемость эффективнее лечится разбиением выражения на несколько переменных чем новыми синтаксическими плюшками.

kitmouse, 29 ноября 2017, 10:30 Antervis, код с оператором "p->[5]" будет читать проще чем код с оператором "(*p)[5]", особенно если "p" - это какое-то длинное выражение. "разыменования второго и более уровней" используются всеми и повсеместно - в виде оператора "->".

kitmouse, 29 ноября 2017, 10:27 К сожалению, я отправил это предложение недописав и не вычитав случайным нажатием какой-то комбинации клавиш.