Здравствуйте, Alexander Pazdnikov, Вы писали:
AP> Здравствуйте, Коллеги.
AP>Помогите, пожалуйста, перегрузить оператор сравнения для std::vector<boost::shared_ptr> чтобы сравнивались не указатели, а объекты по этим указателям.
AP>
Совершенно не понял, почему вы вместо std::lexicographical_compare предлагаете использлвать std::equal, когда последний не сравнивает размеры диапазонов?! Или это вы так стараетесь себе жизнь усложнить?! std::lexicographical_compare вполне подходит для выяснения равны между собой две последовательности или нет.
Здравствуйте, Сыроежка, Вы писали:
С>Совершенно не понял, почему вы вместо std::lexicographical_compare предлагаете использлвать std::equal, когда последний не сравнивает размеры диапазонов?! Или это вы так стараетесь себе жизнь усложнить?! std::lexicographical_compare вполне подходит для выяснения равны между собой две последовательности или нет.
Из-за его стандартной семантики — процитированное:
Returns true if range [first1,last1) compares lexicographically less than the range [first2,last2).
Лично я бы удивился, если бы, к примеру, operator< вдруг стал использоваться для проверки на равенство, а std::max_element с пользовательским предикатом стал бы возвращать минимальный элемент
Здравствуйте, Alexander Pazdnikov, Вы писали:
AP> Здравствуйте, Коллеги.
AP>Помогите, пожалуйста, перегрузить оператор сравнения для std::vector<boost::shared_ptr> чтобы сравнивались не указатели, а объекты по этим указателям.
Здравствуйте, Alexey F, Вы писали:
AF>Здравствуйте, Сыроежка, Вы писали:
С>>Совершенно не понял, почему вы вместо std::lexicographical_compare предлагаете использлвать std::equal, когда последний не сравнивает размеры диапазонов?! Или это вы так стараетесь себе жизнь усложнить?! std::lexicographical_compare вполне подходит для выяснения равны между собой две последовательности или нет. AF>Из-за его стандартной семантики — процитированное: AF>
AF>Returns true if range [first1,last1) compares lexicographically less than the range [first2,last2).
AF>Лично я бы удивился, если бы, к примеру, operator< вдруг стал использоваться для проверки на равенство, а std::max_element с пользовательским предикатом стал бы возвращать минимальный элемент
А причем здесь этот оператор? Вы же используете алгоритм с предикатом. Можно исхитриться и придумать предикат, который будет давать нужный результат. Например, предикат, который будет возращать значение оператора != при каждом нечетном вызове предиката.
Да более того (сразу не посмотрел). lexicographical_compare возвратит true на первых же равных элементах, т.е. предикаты вида { return a == b } не напишешь:
Здравствуйте, Сыроежка, Вы писали:
С>Вы же используете алгоритм с предикатом. Можно исхитриться и придумать предикат, который будет давать нужный результат. Например, предикат, который будет возращать значение оператора != при каждом нечетном вызове предиката.
Для lexicographical_compare придётся писать предикат сложнее a == b: здесь
(код взят из стандарта, только < заменён на cmp) и даже не a != b с отрицанием результата — это ещё больше (помимо вводящего в заблуждение имени) затуманит цель кода для того, кто будет потом в нём разбираться.
Здравствуйте, Alexey F, Вы писали:
AF>Здравствуйте, Сыроежка, Вы писали:
С>>Вы же используете алгоритм с предикатом. Можно исхитриться и придумать предикат, который будет давать нужный результат. Например, предикат, который будет возращать значение оператора != при каждом нечетном вызове предиката. AF>Для lexicographical_compare придётся писать предикат сложнее a == b: AF>здесь
AF>(код взят из стандарта, только < заменён на cmp) и даже не a != b с отрицанием результата — это ещё больше (помимо вводящего в заблуждение имени) затуманит цель кода для того, кто будет потом в нём разбираться.
На самом деле предикат простой и не затуманит. Другое дело, что внутри алгоритма этот предикат вызывается дважды, что, как говорится, не комильфо. А так написать предикат очень просто. Достаточно завести внутри класса логической переменную, при каждом вызове предиката которая будет менть значение на прлтивоположное. При первом вызове предиката сотреть, если эта переменная равна false, то возвращать ее. При втором вызове возвращать условие a != b
То есть оператор функции будет состоять из одной строчки
return ( ( first = !first ) && ( a != b ) );
При этом начальное значение first должно быть равно true
Здравствуйте, Сыроежка, Вы писали:
С>Достаточно завести внутри класса логической переменную, при каждом вызове предиката которая будет менть значение на прлтивоположное. При первом вызове предиката сотреть, если эта переменная равна false, то возвращать ее. При втором вызове возвращать условие a != b
бросай курить, вставай на лыжи!
Здравствуйте, Сыроежка, Вы писали:
С>std::lexicographical_compare вполне подходит для выяснения равны между собой две последовательности или нет.
Не подходит. Лексикографическое сравнение в лучшем случае даст эквивалентность (!(a<b) && !(b<a)), а не равенство (a==b). И нет, эти отношения не обязаны быть согласованы для конкретного типа—аргумента шаблона.
Поэтому таки std::equal, с предварительным сравнением размеров и с кастомным предикатом вида [](x,y) { return *x == *y; }.
Здравствуйте, Alexey F, Вы писали:
AF>Здравствуйте, Сыроежка, Вы писали:
AF>Да более того (сразу не посмотрел). lexicographical_compare возвратит true на первых же равных элементах, т.е. предикаты вида { return a == b } не напишешь: AF>
Я вам уже ответил в другом месте. Но повторю здесь свой ответ.
На самом деле предикат простой и не затуманит. Другое дело, что внутри алгоритма этот предикат вызывается дважды, что, как говорится, не комильфо. А так написать предикат очень просто. Достаточно завести внутри класса логической переменную, при каждом вызове предиката которая будет менть значение на прлтивоположное. При первом вызове предиката смотреть, если эта переменная равна false, то возвращать ее. При втором вызове возвращать условие a != b
То есть оператор функции будет состоять из одной строчки
return ( ( first = !first ) && ( a != b ) );
При этом начальное значение first должно быть равно true
Здравствуйте, Сыроежка, Вы писали:
С>Можно исхитриться и придумать предикат, который будет давать нужный результат. Например, предикат, который будет возращать значение оператора != при каждом нечетном вызове предиката.
Так нельзя делать, потому что предикат должен индуцировать отношение строгого слабого порядка.
Здравствуйте, Centaur, Вы писали:
C>Не подходит. Лексикографическое сравнение в лучшем случае даст эквивалентность (!(a<b) && !(b<a)), а не равенство (a==b). И нет, эти отношения не обязаны быть согласованы для конкретного типа—аргумента шаблона.
Ну, если над типом введены два НЕЗАВИСИМЫХ отношения — равенство (==) и порядок (<), тогда конечно.
Но обычно, всё-таки, если определено <, то все остальные отношения следуют ему: a>b = b<a, a<=b = !(b>a), a!=b = (a<b)||(b<a) и т.д.
Другое дело, что над типом может отсутствовать отношение порядка, а только равенство.
Вот тогда без std::equal или его рукодельных аналогов деваться некуда
template<class I, class J>
bool equal_ranges(I i, I ie, J j, J je)
{
while(i!=ie && j!=je && *i==*j) { ++i; ++j; }
return i==ie && j==je;
}
template<class I, class J, class E>
bool equal_ranges(I i, I ie, J j, J je, E eq)
{
while(i!=ie && j!=je && eq(*i,*j)) { ++i; ++j; }
return i==ie && j==je;
}
Здравствуйте, Сыроежка, Вы писали:
С>А причем здесь этот оператор? Вы же используете алгоритм с предикатом. Можно исхитриться и придумать предикат, который будет давать нужный результат. Например, предикат, который будет возращать значение оператора != при каждом нечетном вызове предиката.
Stateful функция плоха тем, что вызывающая сторона должна специфицировать порядок её вызова.
Вот, например, для for_each этот порядок определён, а для всяких операций сравнения — нет.
Этим пользуются дебажные версии библиотек, проверяя, соответствует ли аргумент-предикат требуемой аксиоматике, а если данные должны быть упорядочены — то заодно, упорядочены ли они на самом деле?
Так что этот фокус с чётностью прокатит только для одной конкретной версии STL, притом, что работать он будет в режиме undefined behavior (которое в частном случае является искомым и желанным поведением...)
Здравствуйте, Кодт, Вы писали:
К>Ну, если над типом введены два НЕЗАВИСИМЫХ отношения — равенство (==) и порядок (<), тогда конечно. К>Но обычно, всё-таки, если определено <, то все остальные отношения следуют ему: a>b = b<a, a<=b = !(b>a), a!=b = (a<b)||(b<a) и т.д.
Не уверен, что это справедливо хотя бы даже для юникодной строки. Ну и вообще любые типы с существенно частичным порядком.
Здравствуйте, Centaur, Вы писали:
C>Не уверен, что это справедливо хотя бы даже для юникодной строки. Ну и вообще любые типы с существенно частичным порядком.
Ну если юникодную строку нарочно сравнивать не через strcmp (wcscmp), а через strcoll, — то конечно.
Это как раз ситуация, когда отношения равенства и порядка будут введены произвольно и без оглядки друг на друга. ССЗБ.
Часто (не знаю, как в жизни, а на форумах точно) люди вводят оператор < над своим типом от балды, просто потому, что это требование какого-нибудь контейнера (std::map) или алгоритма (std::sort). Естественно, что в лучшем случае они заботятся об аксиоматике порядка (и то не всегда, а иногда — да вот прямо здесь! — заботятся о том, чтобы её безболезненно сломать). А об согласовании с другими операторами даже не вспоминают.
Здравствуйте, Alexander Pazdnikov, Вы писали:
AP> Здравствуйте, Коллеги.
AP>Помогите, пожалуйста, перегрузить оператор сравнения для std::vector<boost::shared_ptr> чтобы сравнивались не указатели, а объекты по этим указателям.
Не надо так делать — проблем потом не оберешься.
Сделай отдельную функцию, которая будет этим заниматься.
Здравствуйте, Alexander Pazdnikov, Вы писали:
J>>Не надо так делать — проблем потом не оберешься. AP>Почему не надо так делать? AP>Какие проблемы могу огрести?
Потому что это принципиально разные вещи — равенство указателей и равенство того, на что они указывают.
А уж если ты такое делаешь на следующем уровне (контейнер указателей) — ты нарушаешь сразу кучу самоочевидных инвариантов (а.к.а. нарушение принципа Лисков).
Например, из того, что вектора равны, следует равенство всех элементов, а у тебя это не выполняется, если ты только не захочешь перегрузить заодно и оператор сравнения указателей, но это тоже очень плохо — у тебя указатели на разные типы начнут вести себя по-разному, и обобщенный код, если такой есть, перестанет работать.
AP>Сейчас использую в тестах, AP>BOOST_CHECK_EQUAL(orig_container, computed_container); AP>В случае перегрузки operator== пользуюсь ими стандартными способами.
Нарушение ODR не пугает?
Чтобы всех этих проблем избежать, лучше завернуть этот вектор в класс, у которого ты сделаешь все необходимые методы сравнения и чего угодно еще.
Этот класс будет новой сущностью, НЕ вектором, а стало быть, все рассуждения, применимые к вектору, к нему самому применимы уже не будут.
С другой стороны, вектор, который сидит внутри этого класса, останется нормальным вектором с обычной семантикой, и любой обобщенный код, работающий с векторами, будет правильно с ним работать.
Заодно (по моему собственному опыту), несмотря на то, что это выглядит поначалу просто как примитивная тонкая обертка над вектором, как только ты создашь этот класс, окажется, что в него еще много чего связанного можно запихнуть, а результирующий код станет намного чище, и это именно потому, что твоя основная сущность — это все-таки не вектор, раз у него понадобилось так неочевидно менять семантику.