Универсальный меньше <> для указателей в стандарте C++
Много раз мне понадобился набор указателей. Каждый раз, когда это происходит, я в конечном итоге пишу реализацию less<> для типа указателя - приведу два указателя к size_t и сравню результаты.
Мой вопрос - это доступно в стандарте? Я не мог найти ничего подобного. Похоже, достаточно общий случай...
Обновление: похоже, что в следующем стандарте исправлены все проблемы с пометкой less<> для типов указателей и включенным unordered_set. Через несколько лет этот вопрос станет спорным.
Между тем, текущий стандарт не имеет "легального" решения этого вопроса, но приведение size_t работает.
Обновление для обновления: хорошо, я буду ошеломлен! Не только
std::map<void *, int, std::less<void*> > myMap;
работает, но даже
std::map<void *, int > myMap;
также.
И это в gcc 3.4.1 . Я делал все эти броски даром, и Литб совершенно прав. Даже номер раздела, который он цитирует, точно такой же в текущем стандарте. Ура!
6 ответов
Два указателя можно сравнить с использованием объектов функции сравнения less
, greater
и т. д. В противном случае, используя одеяло operator<
и т. д., это возможно только в том случае, если указатели указывают на элементы одного и того же объекта массива или одного после конца. В противном случае результаты не уточняются.
20.3.3/8
в C++03
Для шаблонов
greater
,less
,greater_equal
, а такжеless_equal
, специализации для любого типа указателя дают общий порядок, даже если встроенные операторы<
,>
,<=
,>=
не делайте.
Не нужно явно специализироваться и вручную приводить size_t
: Это понизит мобильность даже, так как отображение reinterpret_cast
от указателей до целых чисел определяется реализацией и не требует выдачи какого-либо порядка.
Изменить: для более подробного ответа см. Этот.
Нет, это не доступно. Стандарт гласит, что указатели сопоставимы только со встроенными операторами, когда они указывают на тот же массив или другой блок памяти. То есть блок должен быть выделен сразу, в отличие от двух отдельных распределений, которые могут оказаться смежными друг с другом.
Хорошо:
int x[2];
bool b = &x[0] < &x[1];
Это не определено:
int x0;
int x1;
bool b = &x0 < &x1;
Хорошо:
struct foo {
int x0;
int x1;
};
foo f;
bool b = &f.x0 < &f.x1;
Оба значения являются членами одной и той же структуры, поэтому они принадлежат одному и тому же блоку памяти (а именно, f
"S).
С практической точки зрения, однако, нет ничего плохого в вашем пользовательском сравнении.
Тем не менее, ваша специальная специализация не нужна, так как std::less
Шаблон определен для указателей, как видно. Так что все в порядке:
int x0;
int x1;
std::less<int*> compare;
bool b = compare(&x0, &x1);
До сих пор нет никаких указаний на то, каким должен быть результат, но вы по крайней мере обещали получить какой-то результат, в отличие от неопределенного поведения. Вы получаете полный заказ, но вы не знаете, какой заказ, пока не запустите его.
Даже если вы думаете, что сравнение битов указателей безвредно (в конце концов, это просто адреса в памяти, верно?), Есть веские причины, почему языковой стандарт не поощряет это. С одной стороны, если у вас есть код, результаты которого зависят от относительного порядка указателей (например, порядок результатов зависит от порядка итераций по набору или карте указателей), результаты будут нестабильными: изменение версии компилятора или операционной системы релиз может изменить результаты. Воспроизводимость достаточно ценна, чтобы избежать этой нестабильности.
Сравнение указателей - довольно рискованное дело. Сравнивать указатели имеет смысл только в том случае, если они оба указывают на один и тот же блок памяти, в противном случае операция не определена. Поэтому (действительный) набор указателей является довольно специализированной вещью, и нет, в стандартной библиотеке его нет.
Сравнение указателей, которые не размещены в одном и том же блоке, не определено, возможно, из-за того, что есть модели памяти, которые имеют проблемы с ними (и раньше были более распространенными).
Что вам нужно использовать unordered_set<>
, который не требует оператора сравнения. Он находится в следующем стандарте C++ и в то же время доступен в виде Boost-заголовка.
Не hash_set
что вам нужно? Это будет частью следующего стандарта