Можно ли использовать элементы другого типа, чем те, которые содержатся в std :: set, для выполнения поиска и удаления?

Допустим, у меня есть следующее:

struct MetadataThingy {

void *actual_thingy;
int some_metadata;
int more_metadata;

bool operator<(MetadataThingy const& other) const {
return actual_thingy < other.actual_thingy;
}

};

где actual_thingy указывает на некоторые важные данные, и я хочу, чтобы контейнер упорядочен по значению actual_thingy вместо значения указанного элемента, но мне нужно сохранить некоторые другие данные о нем, поэтому я создал класс-оболочку MetadataThingy с компаратором, который учитывает только значение actual_thingy указатель (вместо того, чтобы использовать контейнер void *)

Теперь, учитывая следующий код:

std::set<MetadataThingy> thingy_set;

void test() {

MetadataThingy m1 { nullptr, 5, 20 };
MetadataThingy m2 { &m1, 1, 2 };
MetadataThingy m3 { &m2, 6, 0 };

thingy_set.insert(m1);
thingy_set.insert(m2);
thingy_set.insert(m3);

MetadataThingy m;
m = *thingy_set.find(m2); // OK.
m = *thingy_set.find(static_cast<void *>(&m2)); // Nope. Can't use a pointer.

}

Так как каждый MetadataThingy может быть однозначно идентифицировано по значению указателя, которое он хранит, и упорядочено по значению указателя, имеет смысл найти / удалить объекты просто с помощью void * как ключ. Тем не менее, в настоящее время мне нужно создать манекен MetadataThingy каждый раз, когда я ищу элемент, который кажется действительно грязным. Я уже рассмотрел использование только map с указателями в качестве ключа и MetadataThingy как ценность, но так как каждый MetadataThingy в любом случае также должен содержать указатель, это немного избыточно. Так, есть ли способ использовать элемент типа, отличного от того, который хранится в наборе, для поиска или удаления значений в наборе, учитывая, что элементы двух типов взаимно сопоставимы и что элементы одного типа могут быть однозначно сопоставлены с другим ( void * а также MetadataThingy изоморфны)? (Я не включил ничего в приведенный выше код, но предположим, что существуют операторы с перегрузками для сравнения void * а также MetadataThingy в любом порядке.)

Немного предыстории проблемы, которую я пытаюсь решить, на всякий случай, если кто-то может порекомендовать лучший подход: мне нужно упорядочить коллекцию по нескольким критериям, поэтому у меня есть несколько MetadataThingy контейнеры, все отсортировано по разным критериям. «Метаданные» в этом случае — это то, что мне нужно, чтобы отслеживать положение элементов во всех контейнерах, чтобы я мог быстро удалить их. Это может показаться идеальной работой для многоадресных контейнеров, но порядок этих элементов постоянно меняется, что, по мнению AFAIK, не сработает.

5

Решение

Начиная с C ++ 14, std::set имеет шаблонные версии своих функций поиска find, lower_boundи т. д. Они позволяют передавать любой объект для сравнения, если это поддерживает компаратор.

Это означает, что вы можете напрямую передать void* в findДо тех пор, пока компаратор поддерживает сравнение MetadataThingy а также void*,

Для получения дополнительной информации см. http://en.cppreference.com/w/cpp/container/set/find.

Чтобы понять ограничение относительно Compare::is_transparent, Я нашел этот вопрос StackOverflow очень полезно.

2

Другие решения

Вы можете сделать это с помощью std::find_if и предоставление предиката функтора.

#include <algorithm>

struct Predicate
{
void const * const ptr_;
explicit Predicate(const void* ptr) : ptr_(ptr) {}
bool operator()(const MetadataThingy& other)
{
return ptr_ == other.actual_thingy;
}
};

m = *std::find_if(thingy_set.begin(), thingy_set.end(), Predicate(&m2));

Вы можете использовать итератор, возвращенный std::find_if удалить элемент из набора, передав его set::erase.

1

Нет, подпись map<>::find требует, чтобы вы передали тип ключа.

Однако существует относительно простой обходной путь. использование boost::optional или же std::tr2::optional (из C ++ 1y) для хранения неключевых данных.

struct MetadataThingy {
void* pBlah;
optional<rest_of_stuff> rest;
static MetadataThingy searcher( void* );
MetadataThingy(...);
};

затем позвоните MeatadataThingy::searcher сгенерировать значение вашего ключа.

Другой подход заключается в хранении интеллектуальных (вероятно, уникальных) указателей на подчиненных интерфейсах, каждый из которых имеет метод «получения полных данных». Затем, когда вы хотите выполнить поиск, создайте заглушку подчиненного интерфейса, который возвращает nullptr на «получить полные данные».

struct MetadataFull;
struct MetadataRoot {
virtual MetadataFull* get() = 0;
virtual MetadataFull const* get() const = 0;
virtual ~MetadataRoot() {}
};
template<typename T>
struct MetadataFinal: virtual MetadataRoot {
static_assert( std::is_base_of< T, MetadataFinal<T> >::value, "CRTP failure" );
virtual MetadataFull* get() { return static_cast<T*>(this); }
virtual MetadataFull const* get() const { return static_cast<T const*>(this); }
};
struct MetadataStub: virtual MetadataRoot {
virtual MetadataFull* get() { return nullptr; }
virtual MetadataFull const* get() const { return nullptr; }
};
struct MetaDataA: virtual MetaDataRoot {
void* pBlah;
};

struct MetaDataFull: MetaDataA, MetadataFinal<MetaDataFull> {
// unsorted data
};
struct MetaDataAStub: MetaDataA, MetaDataStub {};

Теперь это можно сделать с virtual функции, но не virtual наследование с небольшой суматохой, если вам это действительно нужно.

1

Библиотека не поддерживает запрашиваемое вами поведение, хотя я видел, как другие люди запрашивали то же самое (т. Е. Предоставляли шаблонную функцию-член find в упорядоченных ассоциативных контейнерах, в которых используется перекрестный компаратор), хотя это нечасто.

Ваш тип необычный в том, что только один из атрибутов члена принимает участие в значение объекта (то есть используется в сравнении). Компилятор не может знать, что только некоторые из членов (или какие из членов) являются частью значения, а какие нет. хотя может быть, что объекты на самом деле не сравнимый а ты просто забил operator< как простой способ включения использования в ассоциативных контейнерах.

Если это так, рассмотрите возможность отказа от operator< это не совсем сравнить MetaThingy объекты, а также изменить структуру данных, чтобы быть std::map<void*,MetaThingy>, что сделало бы дизайн чище за счет дополнительного void* для хранимого объекта — это также может быть случай, когда void* находится внутри MetaThingy для поиска в наборе … в этом случае это может даже иметь больше смысла, и вы могли бы предоставить std::map<void*,MetaInfo>,

1
По вопросам рекламы [email protected]