Рассмотрим следующий фрагмент:
#include <bits/stdc++.h>
using namespace std;
int main() {
int x=3;
int y=1;
int z=2;
cout<<(&x)<<' '<<(&y)<<' '<<(&z)<<endl;
set<reference_wrapper<int>> a;
a.insert(ref(x));
a.insert(ref(y));
a.insert(ref(z));
for(const auto& i: a) cout<<i<<' '<<endl;
y=10;
for(const auto& i: a) cout<<i<<' ';
return 0;
}
Что произойдет с базовым контейнером, если будет изменено свойство, используемое контейнером для сортировки?
Edit1: Судя по тому, что я могу попробовать, запустив код, порядок неправильный, но, поскольку он содержал ссылку, значение обновляется правильно. Итак, следует быть осторожным при использовании reference_wrapper внутри контейнеров (особенно там, где мутации могут привести к тому, что контейнер сделает недействительными его утверждения)?
Что касается вашего «Edit1», пожалуйста, постарайтесь, чтобы вопрос был кратким. SO не похож на форум с постоянными дискуссиями. Нет необходимости читать вопрос, читать ответ и продолжать читать вопрос, чтобы понять, о чем здесь спрашивают.
«Это плохая практика…» основано на мнениях.
Если вы собираетесь использовать ссылочные оболочки, вы можете с таким же успехом хранить указатели, не принадлежащие (никакой реальной семантической разницы и проще в использовании). Большая проблема со ссылками (и указателями) заключается в том, что очень легко выстрелить себе в ногу. Представьте, что x
, y
, z
являются локальными переменными функции, и вы возвращаете набор ссылок... Так что нет, это неплохая практика, но вам нужно быть осторожным. функция для сортировки указателей
Обратите внимание, что вы, вероятно, не захотите использовать std::set
, но std::vector<int*>
@ 463035818_is_not_an_ai Очевидно, ключи следует сохранять константными. Я просто пытаюсь сказать, что, поскольку нет ограничений на тип ключа, можно столкнуться с этой проблемой. Более того, я пытаюсь проверить, следует ли считать использование reference_wrapper в контейнерах плохой практикой или следует быть осторожным?
это действительно интересная часть вопроса, на которую у меня тоже нет ответа. Обратите внимание, что std::set
не предоставляет неконстантного доступа к элементам. std::set::iterator
— постоянный итератор. Также обратите внимание, что на самом деле вы не изменяете элементы, а const
, но вы изменяете объекты, на которые они ссылаются. Проблема в компараторе. Мне не известен какой-либо формализм, который требует, чтобы когда вы делаете auto x = comp(a,b);
, что-то делали (но не вставляли и не удаляли элементы), auto y = comp(a,b);
чтобы x
и y
были одинаковыми.
но кто-то другой знает :D
Понял, спасибо! @463035818_is_not_an_ai
Где вы учились #include <bits/stdc++.h>
? Заблокируйте этот сайт. Сожгите эту книгу. Удалить этого друга из друзей. Изучите C++ по хорошей книге по C++.
Что произойдет с базовым контейнером, если будет изменено свойство, используемое контейнером для сортировки?
Любое дальнейшее использование контейнера, требующее сравнения этого элемента, имеет неопределенное поведение, поскольку вы нарушаете это требование:
Для любых двух ключей
k1
иk2
в одном контейнере вызовcomp(k1, k2)
всегда будет возвращать одно и то же значение.
Понял, спасибо! Как можно не вникать в эти проблемы? Следует ли нам избегать использования reference_wrapper в ключах ассоциативных контейнеров и неупорядоченных ассоциативных контейнеров?
Если ответ на вышеприведенный вопрос положительный, то разве стандарт не должен вообще позволять его создавать? Можно быть осторожным, но я думаю, что в большой кодовой базе гораздо проще столкнуться с этой проблемой.
@Рупа std::reference_wrapper
это не волшебство. Создать подобные class
может каждый. Ключевой принцип — не создавать висячие ссылки. Текущее состояние C++ не может помешать пользователю сделать это.
@Rupa Если бы стандарт блокировал std::reference_wrapper
в наборах, то кто-то бы просто написал lib::reference_wrapper
. Чтобы заблокировать это, вам придется запретить любой класс с любыми элементами данных-указателями. Вам просто нужно быть бдительным в отношении модификаций
@Red.Wave проблема здесь не висит, но, как правило, любое косвенное обращение, позволяющее изменить то, что сравнивает компаратор, имеет ту же проблему.
@463035818_is_not_an_ai постоянство — это лишь часть проблемы; предоставление правильного аргумента компаратора (например, компаратора адреса) может решить проблему постоянства. Неизбежную проблему висения невозможно решить, если не избегать reference_wrappper
.
@Red.Wave но вопрос не в висящей ссылке. Конечно, могут быть проблемы, с которыми ОП еще не сталкивался, и важно знать о висячих ссылках. Ваш комментарий выглядит так, как будто отказ от висячих ссылок решит проблему с нарушенной сортировкой, но это не так. Возможно, я просто преувеличиваю, хотя именно это я и имею в виду.
@Red.Wave не ограничивается указателями и ссылками, член данных mutable
также не подходит для формирования (части) сравнения
Компаратор @Caleth всегда должен быть определен, чтобы не полагаться на что-либо изменяемое. Но, как я упоминал ранее, хранение ссылок, не являющихся владельцами, например std::reference_wrapper
, вызывает проблемы с висячими ссылками/указателями; Ссылки, не являющиеся владельцами, приветствуются только в немедленно выполняемых выражениях, и текущие спецификации ядра C++ не могут отличить их от остальных.
@Red.Wave или будьте осторожны с жизнью. В коде OP нет никаких висячих элементов, потому что символы int
переживают набор
@Калет не посылай человека выполнять работу машины. Всякий раз, когда вы передаете ответственность человеку, появляются ошибки. Все эти идиомы программирования созданы для того, чтобы передать работу от человека к машине. В противном случае нет смысла использовать конструкции высокого уровня.
@Red.Wave, кажется, ты утверждаешь, что ссылки опасны. Согласен, но иногда нужна ссылочная семантика. Невыполнение требований также является ошибкой.
@Caleth Я утверждаю, что требования могут быть выполнены, но это не предотвращает ошибки. Широкое использование std::reference_wrapper
— это бомба замедленного действия.
вам удалось сломать инвариант. Ключи в
std::set
должны бытьconst
. Как и почти во всем, есть способы обойти это ограничение, но в результате вы получите сломанный набор.