С ++, учебник для начинающих, 5 изд. : использование сортировки по вектору указателей не определено? - PullRequest
3 голосов
/ 26 апреля 2020

В C ++ учебник для начинающих 5 изд. стр. 172:

"Мы не можем использовать реляционные операторы в указателях на два несвязанных объекта:

int i = 0, sz = 42;
int *p = &i, *e = &sz;// undefined: p and e are unrelated; comparison is meaningless!    
while (p < e)  ".

Но если я запускаю один и тот же код в G CC или CLANG, он прекрасно компилируется даже против wall flag.

  • На странице 712: об объектах функций:

"Одним из важных аспектов этих объектов библиотечных функций является то, что библиотека гарантирует, что они будут работать для указателей. Напомним, что сравнение двух не связанных между собой указателей не определено (п. 3.5.3, с. 120). Тем не менее, мы можем отсортировать вектор указателей по их адресам в памяти. Хотя для нас было бы неопределенным делать это напрямую, мы можем сделать это через один из объектов библиотечной функции:

vector<string *> nameTable;  // vector of pointers

// ошибка: указатели в nameTable не связаны, поэтому

sort(nameTable.begin(), nameTable.end(),
 [](string *a, string *b) { return a < b; });

// ok: библиотека гарантирует, что меньше указателей на типы указателей правильно определены

sort(nameTable.begin(), nameTable.end(), less<string*>());

Стоит также отметить, что ассоциативные контейнеры используют less<key_type> для упорядочения своих элементов. В результате мы можем определить set указателей или использовать указатель в качестве ключа в map без указания меньшего напрямую. "

  • Я не знаю, как less<string*> объект библиотечной функции гарантирует сравнение, хотя он применяет < только к аргументам типа элемента ?!

  • В его примере nameTable; это вектор указателей на -строки, таким образом, они являются смежными в памяти, так как это может быть неопределенным при использовании < в sort, но "четко определенным" при использовании std::less<string*>?

Ответы [ 3 ]

3 голосов
/ 26 апреля 2020

Я хотел бы расширить мой комментарий сверху:

вам не обязательно знать, как на самом деле работает специализация std :: less для указателей. Важно то, что он там и делает правильные вещи

Я не хотел подразумевать, что ваш вопрос не заслуживает ответа, а скорее это то, что стандарт C ++ определяет :

Для шаблонов less, more, less_equal и more_equal специализации для любого типа указателя дают результат, соответствующий строгому общему порядку, определенному реализацией указателей ([defns.order.ptr ]). [... Примечание ...]

Для специализаций шаблонов less, more, less_equal и more_equal, если оператор вызова вызывает встроенный оператор, сравнивающий указатели, оператор вызова выдает результат, соответствующий реализации -определенный строгий общий порядок по указателям.

Стандарт не упоминает, как реализуются специализации std::less (или других сравнений) для указателей. Это только указывает, что их «оператор вызова выдает результат, совместимый с определенным реализацией строгим общим порядком по указателям». Для этого реализации не всегда могут использовать встроенный оператор <, потому что [expr.rel # 4] :

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

(4.1) Если два указателя указывают на разные элементы одного и того же массива или его подобъектов, указатель на элемент с более высоким индексом требуется для сравните больше.

(4.2) Если два указателя указывают на разные нестатические c элементы данных одного и того же объекта или на подобъекты таких элементов, рекурсивно, указатель на объявленный позже элемент требуется для сравните больше, если два члена имеют одинаковый контроль доступа ([class.access]), ни один из них не является подобъектом нулевого размера, а их класс не является объединением.

(4.3) В противном случае ни один указатель не является требуется сравнивать больше, чем другие.

То есть: указатели, которые не являются указателями на один и тот же массив или указатели подобъектам одного и того же объекта нельзя переносимо сравнить со встроенным <.

Я не знаю, как less<string*> объект библиотечной функции гарантирует сравнение, хотя применяется только < в аргументы типа элемента ?!

Это не так. Вернее, это не обязательно. Ваш компилятор знает, может ли в целевой архитектуре < использоваться для произвольных указателей, но в общем случае это не так.

В его примере nameTable; это вектор указателей на -строки, таким образом, они являются смежными в памяти, так как это может быть неопределенным при использовании < в сортировке, но "четко определенным" при использовании std::less<string*>?

Это небольшое недоразумение. Действительно, std::vector хранит свой элемент в смежной памяти, но указатели, которые вы храните в векторе, могут указывать куда угодно. Следовательно, они не являются ни указателями на элементы одного и того же массива, ни указателями на подобъекты одного и того же объекта. Ваш аргумент был бы верен, если бы у вас было std::vector<string> и вы хотите сравнить указатели с элементами в этом контейнере, но это не то, что вы делаете здесь.

2 голосов
/ 26 апреля 2020

Я не знаю, как less<string*> объект библиотечной функции гарантирует сравнение, хотя он применяет < только к аргументам типа элемента?!

Стандарт не определяет msgstr "это только < применяется к аргументам типа элемента". Ваша библиотека C ++ может сделать это, но существуют и другие возможности. Дело в том, что если вы переключаетесь на другую платформу, специализация std:less для указателей может потребовать большего, чем просто применение <. Вы защищены от необходимости знать эту деталь; стандартные заголовки для этой архитектуры позаботятся о вас, пока вы используете std::less.

Использование std::less является переносимым; использование < - это не так.

В его примере nameTable; это вектор указателей на строки, поэтому они непрерывны в памяти, так как это может быть неопределенным при использовании < в сортировке, но "четко определенным" при использовании std::less<string*>?

Смежные указатели не имеют значения. Что важно, так это объекты, на которые указывают. Если у вас есть массив строк, назовем его array, и если каждый элемент nameTable указывает на элемент array, то порядок через < определяется стандартом C ++. Однако, если строки, на которые указывают, не все из одного массива, то порядок с помощью < не гарантируется. Это может сработать, но у вас нет гарантии.

0 голосов
/ 26 апреля 2020

Этот ответ содержит ложную информацию. Я не удаляю его, потому что комментарии очень полезны.

Что означает книга, так это то, что сравнивать адрес несвязанных объектов не имеет смысла. Вы можете написать код, который делает это, но это не имеет смысла.

Если вы используете контейнер, который сортирует свое содержимое по указателям, ну, порядок будет согласованным (в пределах одного прогона), но не очень полезный заказ. Если адрес объекта A меньше, чем объект B, он останется меньше. Так что, это стабильно, это будет работать. Но это ничего не говорит вам об A или B.

И при следующем запуске A и B могут поменяться местами в памяти и дать вам другой порядок.

Редактировать: см. Другой ответы; -)

Редактировать: забавные случаи, на которые я наткнулся, погуглив тему: https://quuxplusone.github.io/blog/2019/01/20/std-less-nightmare/

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...