Здравствуйте, Marty, Вы писали:
M>то я же получу не mapped_type, а Proxy, а со ссылкой вообще висяк получу. M>Как это сейчас разруливается?
Поскольку на уровне языка различить — прокси это или окончательное значение — невозможно, то без лишней писанины и административных мер — никак.
Первая административная мера — придать проксям семантику указателя.
И тогда явно
auto p = map[key];
auto v = *p;
Заодно, это решает проблему множественного опосредования. Сколько раз опосредовано, столько звёздочек напихать.
Вторая административная мера даёт больше писанины в одних местах и меньше — в других.
Это ввести концепт "прокси" и функцию распаковки
template<class T> concept Proxy = .....;
template<class T> using proxee_t = .....;
template<Proxy T> auto proxee(T&& p) -> proxee_t<T> { ..... }
// и дефолтная реализация этой функции для не-прокси-типовtemplate<class T> auto&& proxee(T&& v) { return std::forward<T>(v); }
(дизайн этих шаблонов не буду сейчас приводить, там есть над чем разнообразно подумать, но это заслонит идею)
и пользоваться
auto v = proxee(map[key]);
С одной стороны, это больше символов, чем звёздочка.
С другой, получается унифицированный код с не-прокси-типами, и не требуется напрямую менять апи уже существующих прокси.
Грубо говоря, мы введём внешние трейтсы, специализируем их для конкретных прокси-типов (даже введём вспомогательные концепты, позволяющие отличить наши прокси-типы), и положим эти специализации где-то рядом.
Правда, надо заметить, что такие специализации чреваты невменяемыми ошибками нарушения ODR, особенно, на концептах.
То есть, ключевые специализации должны оказаться как можно ближе к объявлению прокси-типа, и чтобы никто не вклинился между ними и не инстанцировал основную дефолтную версию концепта и-или функции.
Здравствуйте, Marty, Вы писали:
M>Порядок обхода map определяется порядком добавления ключей. Если ключ не добавляется, а перезаписывает имеющееся значение, то могут быть варианты: а) запретить (кинуть исключение) б) обновить на месте в) переместить в конец
Логика мне непонятна (как и многое из того, что вы здесь показываете).
Такое впечатление, что вы в очередной раз делаете что-то странное, поэтому и отгребаете приключения.
Думается, что если map -- это ваш контейнер, то вы можете просто запретить его operator[] возвращать неконстантную ссылку.
Тогда у вас не получится заменить значение по уже имеющемуся ключу. Для такой замены нужно будет использовать другой метод. Внутри которого вы сможете делать что вам захочется (хоть обновлять, хоть перемещать в конец). И никакие прокси вам не потребуются.
Здравствуйте, Marty, Вы писали:
M>>>Ну, вообще-то я как раз хочу сделать так, чтобы это было невозможно, если ты не заметил
S>>Не заметил.
M>Ты какой-то ненаблюдательный
Это вы здесь задаете вопросы в надежде что вам подскажут. Значит внятно и понятно излагать ваши проблемы -- это в ваших интересах.
Из описанного вами я увидел, что у вас из map::operator[] возвращается Proxy, у которого есть оператор присваивания.
Сам факт наличия этого оператора говорит о том, что у вас в дизайне заложено нарушения принципа минимального удивления.
Но из описанного так же видно, что вы хотите этот самый Proxy с этим самым нарушением сохранить. Просто вам нужно как-то подружить его с auto из современного С++. Т.е. пусть хитровывернутый дизайн останется, но без возможности получить повисшие ссылки из-за auto.
Тут вопрос такой — в общем случае пользователь же не догадывается, что operatorp[] возвращает прокси, и привыкши писать auto с лёгким сердцем это сделает.
Семантика указателя, поломает способ с auto при следующей попытке использования auto переменной. Но. Я прокси делаю для того, чтобы отловить и специальным образом обработать ситуацию
Здравствуйте, so5team, Вы писали:
M>>Но. Я прокси делаю для того, чтобы отловить и специальным образом обработать ситуацию M>>
M>>map[key] = value;
M>>
S>Интересно, а что можно ловить в этой ситуации? И, главное, зачем?
Порядок обхода map определяется порядком добавления ключей. Если ключ не добавляется, а перезаписывает имеющееся значение, то могут быть варианты: а) запретить (кинуть исключение) б) обновить на месте в) переместить в конец
Здравствуйте, so5team, Вы писали:
M>>Порядок обхода map определяется порядком добавления ключей. Если ключ не добавляется, а перезаписывает имеющееся значение, то могут быть варианты: а) запретить (кинуть исключение) б) обновить на месте в) переместить в конец
S>Логика мне непонятна (как и многое из того, что вы здесь показываете).
Я заметил
S>Такое впечатление, что вы в очередной раз делаете что-то странное, поэтому и отгребаете приключения.
Возможно
S>Думается, что если map -- это ваш контейнер, то вы можете просто запретить его operator[] возвращать неконстантную ссылку.
Контейнер мой, но запретить — не могу
S>Тогда у вас не получится заменить значение по уже имеющемуся ключу. Для такой замены нужно будет использовать другой метод. Внутри которого вы сможете делать что вам захочется (хоть обновлять, хоть перемещать в конец). И никакие прокси вам не потребуются.
Контейнер мой, но передаётся в чужой код, который кладёт значения в map через operator[]
Здравствуйте, Marty, Вы писали:
S>>Логика мне непонятна (как и многое из того, что вы здесь показываете).
M>Я заметил
А могли бы и задуматься почему ваши гениальные замыслы не находят понимания.
S>>Тогда у вас не получится заменить значение по уже имеющемуся ключу. Для такой замены нужно будет использовать другой метод. Внутри которого вы сможете делать что вам захочется (хоть обновлять, хоть перемещать в конец). И никакие прокси вам не потребуются.
M>Контейнер мой, но передаётся в чужой код, который кладёт значения в map через operator[]
Значит и вы, и ваши пользователи должны страдать, ибо нарушается принцип наименьшего удивления.
Если хочется извращений, то попробуйте поэкспериментировать с методами вашего прокси, которые будут разрешены только для случая rvalue reference.
Что-то типа:
class proxy {
public:
void f() &&;
...
};
Тогда метод f() можно будет вызвать у объекта proxy только если это временный объект.
А вот если кто-то его сохранил на стеке и пытается вызвать f по обычной или конст-ссылке, то компилятор ударит по рукам: https://wandbox.org/permlink/s5hlLuSfHKbEIg0g
Здравствуйте, so5team, Вы писали:
S>>>Логика мне непонятна (как и многое из того, что вы здесь показываете).
M>>Я заметил
S>А могли бы и задуматься почему ваши гениальные замыслы не находят понимания.
Ну, они мало у кого не находят понимания
S>>>Тогда у вас не получится заменить значение по уже имеющемуся ключу. Для такой замены нужно будет использовать другой метод. Внутри которого вы сможете делать что вам захочется (хоть обновлять, хоть перемещать в конец). И никакие прокси вам не потребуются.
M>>Контейнер мой, но передаётся в чужой код, который кладёт значения в map через operator[]
S>Значит и вы, и ваши пользователи должны страдать, ибо нарушается принцип наименьшего удивления.
И в чем же нарушение?
S>Если хочется извращений, то попробуйте поэкспериментировать с методами вашего прокси, которые будут разрешены только для случая rvalue reference. S>Что-то типа: S>
S>Тогда метод f() можно будет вызвать у объекта proxy только если это временный объект. S>А вот если кто-то его сохранил на стеке и пытается вызвать f по обычной или конст-ссылке, то компилятор ударит по рукам: https://wandbox.org/permlink/s5hlLuSfHKbEIg0g
Да, это выглядит интересно. А как такая конструкция называется, и в каких разделах описана?
Здравствуйте, Marty, Вы писали:
S>>Значит и вы, и ваши пользователи должны страдать, ибо нарушается принцип наименьшего удивления.
M>И в чем же нарушение?
В том, что после того, как вы нашли объект в контейнере и делаете что-то с этим самым объектом (не с контейнером, и не с ключом объекта), то у вас меняется видимое пользователю содержимое объекта.
Т.е. вполне можно понять, когда сам по себе operator[] для map может изменить порядок следования (скажем, если operator[] обновляет "метку времени" последнего обращения к объекту). Но когда это делает не operator[], а последующие действия с самим объектом, то это из категории "внезапно".
Кроме того, как я понимаю, у вас вот такая ситуация:
struct A {
int _a;
long _b;
};
Marty::TrickyMap<int, A> map;
...
map[ 0 ] = A{ ._a = 3, ._b = 4 };
будет менять порядок следования элементов, т.к. вы видите полную замену содержимого. Но вот так:
map[ 0 ]->_a = 3;
map[ 0 ]->_b = 4;
порядок следования уже не поменяется. Хотя объект полностью изменит свое содержимое.
M>Да, это выглядит интересно. А как такая конструкция называется, и в каких разделах описана?
Здравствуйте, so5team, Вы писали:
S>>>Значит и вы, и ваши пользователи должны страдать, ибо нарушается принцип наименьшего удивления.
M>>И в чем же нарушение?
S>В том, что после того, как вы нашли объект в контейнере и делаете что-то с этим самым объектом (не с контейнером, и не с ключом объекта), то у вас меняется видимое пользователю содержимое объекта.
S>Т.е. вполне можно понять, когда сам по себе operator[] для map может изменить порядок следования (скажем, если operator[] обновляет "метку времени" последнего обращения к объекту). Но когда это делает не operator[], а последующие действия с самим объектом, то это из категории "внезапно".
Ну, вообще-то я как раз хочу сделать так, чтобы это было невозможно, если ты не заметил
S>Кроме того, как я понимаю, у вас вот такая ситуация: S>
S>struct A {
S> int _a;
S> long _b;
S>};
S>Marty::TrickyMap<int, A> map;
S>...
S>map[ 0 ] = A{ ._a = 3, ._b = 4 };
S>
S>будет менять порядок следования элементов, т.к. вы видите полную замену содержимого. Но вот так: S>
S>map[ 0 ]->_a = 3;
S>map[ 0 ]->_b = 4;
S>
S>порядок следования уже не поменяется. Хотя объект полностью изменит свое содержимое.