невозможно применить std :: set_intersection к различным типам структур с общим полем

Я пытаюсь использовать использование станд :: set_intersection найти общие элементы между двумя совершенно разными типами структур данных, которые имеют общее связующее поле «имя».

Я посмотрел на следующее введите описание ссылки здесь но, кажется, вынуждает меня выполнить маршрут между двумя различными типами структур, которых я пытался избежать (так как эти типы принадлежат третьей стороне)

Фрагмент кода ниже показывает, чего я пытаюсь достичь.

// common field used for set intersection
typedef struct StructA {
std::string mCommonField;
float mFloatValue;
} StructA;

typedef struct StructB {
std::string mCommonField;
int mValue1;
short mValue2;
} StructB;

// initially unsorted list
std::vector<StructA> aStructs = {{"hello", 1.0f}, {"goodbye", 2.0f}, {"foo", 3.0f}};
// initially unsorted list
std::vector<StructB> bStructs = {{"hello", 1, 2}, {"goodbye", 3, 4}, {"bar", 5, 6}};
// sorting both sets before calling std::intersection
std::sort(aStructs.begin(), aStructs.end(),
[](const StructA& lhs, const StructA& rhs) {
return lhs.mCommonField < rhs.mCommonField;
});
std::sort(bStructs.begin(), bStructs.end(),
[](const StructB& lhs, const StructB& rhs) {
return lhs.mCommonField < rhs.mCommonField;
});

std::vector<StructA> intersection;
std::set_intersection(
aStructs.begin(), aStructs.end(),
bStructs.begin(), bStructs.end(),
std::back_inserter(intersection),
[](const StructA& lhs, const StructB& rhs){
return lhs.mCommonField < rhs.mCommonField;
});

Я использую Visual Studio 2013 для компиляции вышеупомянутого, однако приведенный выше код выдает множество ошибок, как показано ниже. Читая через станд :: set_intersection У меня проблема с сборкой совместимого StrictWeakOrdering comp последний аргумент. В идеале я хотел бы реализовать это как один лямбда.

template <class InputIterator1, class InputIterator2, class OutputIterator,
class StrictWeakOrdering>
OutputIterator set_intersection(InputIterator1 first1, InputIterator1 last1,
InputIterator2 first2, InputIterator2 last2,
OutputIterator result,
StrictWeakOrdering comp);

1> C: \ Program Files (x86) \ Microsoft Visual Studio
12.0 \ VC \ include \ алгоритма (3591): ошибка C2664: ‘bool (__vectorcall *) (const main :: StructA &, const main :: StructB &) ‘: невозможно преобразовать аргумент 1 из’ main :: StructB ‘в’ const main :: StructA &’1>
Причина: невозможно преобразовать из ‘main :: StructB’ в ‘const main :: StructA’
1> Нет определенного оператора преобразования, доступного, который может
выполнить это преобразование, или оператор не может быть вызван 1>
C: \ Program Files (x86) \ Microsoft Visual Studio
12.0 \ VC \ include \ алгоритма (3625): см. Ссылку на создание экземпляра шаблона функции ‘_OutIt
станд :: _ Set_intersection<_InIt1, _InIt2, _OutIt, _Pr> (_ Init1, _InIt1, _InIt2, _InIt2, _OutIt, _Pr)»
компилируется 1> с 1> [1>
_OutIt = станд :: back_insert_iterator >>
1>, _InIt1 = main :: StructA * 1>,
_InIt2 = main :: StructB * 1>, _Pr = main :: 1>] 1> C: \ Program Files (x86) \ Microsoft Visual Studio
12.0 \ VC \ include \ алгоритма (3654): см. Ссылку на создание экземпляра шаблона функции ‘_OutIt
станд :: _ Set_intersection2 (_InIt1, _InIt1, _InIt2, _InIt2, _OutIt, _Pr, станд :: true_type)»
компилируется 1> с 1> [1>
_OutIt = станд :: back_insert_iterator >>
1>, _Pr = main ::
1>, _InIt1 = main :: StructA * 1>,
_InIt2 = main :: StructB * 1>] 1> …. \ src \ dlf \ main.cpp (111): см. Ссылку на шаблон функции
экземпляр «_OutIt
станд :: set_intersection >>, станд :: _ Vector_iterator >>, станд :: back_insert_iterator >>, главная ::> (_ Init1, _InIt1, _InIt2, _InIt2, _OutIt, _Pr)»
компилируется 1> с 1> [1>
_OutIt = станд :: back_insert_iterator >>
1>, _Ty = main :: StructA 1>,
_InIt1 = станд :: _ Vector_iterator >>
1>,
_InIt2 = станд :: _ Vector_iterator >>
1>, _Pr = main ::
1>]

Я также пытался использовать собственную структуру компаратора для сравнения, но ошибки были еще более запутанными:

struct comparator {
bool operator()(const StructA& lhs, const StructB& rhs) const {
return lhs.mCommonField < rhs.mCommonField;
}
bool operator()(const StructB& lhs, const StructA& rhs) const {
return lhs.mCommonField < rhs.mCommonField;
}
};

std::vector<StructA> intersection;
std::set_intersection(
aStructs.begin(), aStructs.end(),
bStructs.begin(), bStructs.end(),
std::back_inserter(intersection),
comparator());

что привело к следующему подробному выводу ошибки. Я надеялся избежать настройки структур (так как фактические, которые я пытаюсь использовать, от сторонних производителей), чтобы иметь конвертеры из StructA в StructB и наоборот, есть ли способ избежать этого и просто иметь простую лямбду добиться простой привязки между 2 относительно простыми структурами с общим полем?
Заранее спасибо.

1> C: \ Program Files (x86) \ Microsoft Visual Studio
12.0 \ VC \ include \ xutility (521): ошибка C2664: ‘bool main ::compara :: operator () (const main :: StructA &, Const
главная :: StructB &) const ‘: невозможно преобразовать аргумент 1 из
‘main :: StructA’ to ‘const main :: StructB &’1> Причина: не может
преобразовать из ‘main :: StructA’ в ‘const main :: StructB’ 1> Нет
пользовательский оператор преобразования доступен, который может выполнить это
преобразование или оператор не может быть вызван 1> C: \ Program
Файлы (x86) \ Microsoft Visual Studio 12.0 \ VC \ include \ xutility (625):
см. ссылку на создание шаблона функции ‘bool
станд :: _ Debug_lt_pred<_Pr, главная :: StructA&, Главная :: StructA&> (_ Pr, _Ty1, _Ty2, станд :: _ Dbfile_t, станд :: _ Dbline_t)»
компилируется 1> с 1> [1>
_Pr = основной :: компаратор 1>, _Ty1 = основной :: StructA & 1>, _Ty2 = main :: StructA & 1>] 1> C: \ Program Files (x86) \ Microsoft Visual Studio 12.0 \ VC \ include \ xutility (636): см.
ссылка на экземпляр шаблона функции ‘void
станд :: _ Debug_order2<_Init, _Pr> (_ FwdIt, _FwdIt, _Pr, станд :: _ Dbfile_t, станд :: _ Dbline_t, станд :: forward_iterator_tag)»
компилируется 1> с 1> [1>
_Init = станд :: _ Vector_iterator >>
1>, _Pr = main :: компаратор 1>,
_FwdIt = станд :: _ Vector_iterator >>
1>] 1> C: \ Program Files (x86) \ Microsoft Visual
Studio 12.0 \ VC \ include \ алгоритма (3649): см. Ссылку на функцию
создание шаблона ‘void
станд :: _ Debug_order<_InIt1, _Pr> (_ InIt, _init, _Pr, станд :: _ Dbfile_t, станд :: _ Dbline_t)»
компилируется 1> с 1> [1>
_InIt1 = станд :: _ Vector_iterator >>
1>, _Pr = main :: компаратор 1>,
_Init = станд :: _ Vector_iterator >>
1>] 1> …. \ src \ dlf \ main.cpp (118): см. Ссылку
функционировать шаблон создания ‘_OutIt
станд :: set_intersection >>, станд :: _ Vector_iterator >>, станд :: back_insert_iterator >>, главная :: компаратор> (_ Init1, _InIt1, _InIt2, _InIt2, _OutIt, _Pr)»
компилируется 1> с 1> [1>
_OutIt = станд :: back_insert_iterator >>
1>, _Ty = main :: StructA 1>,
_InIt1 = станд :: _ Vector_iterator >>
1>,
_InIt2 = станд :: _ Vector_iterator >>
1>, _Pr = основной :: компаратор 1>] 1> C: \ Program Files
(x86) \ Microsoft Visual Studio 12.0 \ VC \ include \ xutility (523): ошибка
C2664: ‘bool main :: компаратор :: operator () (const main :: StructA &, Const
главная :: StructB &) const ‘: невозможно преобразовать аргумент 1 из
‘main :: StructA’ to ‘const main :: StructB &’1> Причина: не может
преобразовать из ‘main :: StructA’ в ‘const main :: StructB’ 1> Нет
пользовательский оператор преобразования доступен, который может выполнить это
преобразование, или оператор не может быть вызван 1> C: \ Program Files
(x86) \ Microsoft Visual Studio 12.0 \ VC \ include \ xutility (521): ошибка
C2664: ‘bool main :: компаратор :: operator () (const main :: StructA &, Const
главная :: StructB &) const ‘: невозможно преобразовать аргумент 2 из
‘main :: StructB’ to ‘const main :: StructA &’1> Причина: не может
преобразовать из ‘main :: StructB’ в ‘const main :: StructA’ 1> Нет
пользовательский оператор преобразования доступен, который может выполнить это
преобразование или оператор не может быть вызван 1> C: \ Program
Файлы (x86) \ Microsoft Visual Studio 12.0 \ VC \ include \ xutility (625):
см. ссылку на создание шаблона функции ‘bool
станд :: _ Debug_lt_pred<_Pr, главная :: StructB&, Главная :: StructB&> (_ Pr, _Ty1, _Ty2, станд :: _ Dbfile_t, станд :: _ Dbline_t)»
компилируется 1> с 1> [1>
_Pr = основной :: компаратор 1>, _Ty1 = основной :: StructB & 1>, _Ty2 = main :: StructB & 1>] 1> C: \ Program Files (x86) \ Microsoft Visual Studio 12.0 \ VC \ include \ xutility (636): см.
ссылка на экземпляр шаблона функции ‘void
станд :: _ Debug_order2<_Init, _Pr> (_ FwdIt, _FwdIt, _Pr, станд :: _ Dbfile_t, станд :: _ Dbline_t, станд :: forward_iterator_tag)»
компилируется 1> с 1> [1>
_Init = станд :: _ Vector_iterator >>
1>, _Pr = main :: компаратор 1>,
_FwdIt = станд :: _ Vector_iterator >>
1>] 1> C: \ Program Files (x86) \ Microsoft Visual
Studio 12.0 \ VC \ include \ алгоритма (3650): см. Ссылку на функцию
создание шаблона ‘void
станд :: _ Debug_order<_InIt2, _Pr> (_ InIt, _init, _Pr, станд :: _ Dbfile_t, станд :: _ Dbline_t)»
компилируется 1> с 1> [1>
_InIt2 = станд :: _ Vector_iterator >>
1>, _Pr = main :: компаратор 1>,
_Init = станд :: _ Vector_iterator >>
1>] 1> C: \ Program Files (x86) \ Microsoft Visual Studio
12.0 \ VC \ include \ xutility (523): ошибка C2664: ‘bool main ::compara :: operator () (const main :: StructA &, Const
главная :: StructB &) const ‘: невозможно преобразовать аргумент 2 из
‘main :: StructB’ to ‘const main :: StructA &’1> Причина: не может
преобразовать из ‘main :: StructB’ в ‘const main :: StructA’ 1> Нет
пользовательский оператор преобразования доступен, который может выполнить это
преобразование, или оператор не может быть вызван

6

Решение

Благодаря выразительности C ++ есть несколько способов решить эту проблему. Следующее ни в коем случае не является исчерпывающим списком.

1. Неявно преобразуйте оба типа в структуру-оболочку для сравнения.

Если вы привязаны к использованию лямбда-выражений, определите тип, который может быть неявно создан из StructA а также StructB и оборачивает поля, используемые для сравнения. Это может позволить выполнить дополнительную логику для полей в конструкторе перед сравнением. Например:

struct Common {
std::string const& mCommonField;
Common(StructA const& sa) : mCommonField{sa.mCommonField} {};
Common(StructB const& sb) : mCommonField{sb.mCommonField} {};
};

Тогда ваше сравнение лямбда может быть написано

auto cmp = [](Common const& lhs, Common const& rhs) {
return lhs.mCommonField < rhs.mCommonField;
};

и используется как

std::sort(aStructs.begin(), aStructs.end(), cmp);
std::sort(bStructs.begin(), bStructs.end(), cmp);
// ...
std::set_intersection(aStructs.begin(), aStructs.end(),
bStructs.begin(), bStructs.end(),
std::back_inserter(intersection),
cmp
);

Живой пример на Coliru Viewer.

2. Используйте компаратор с шаблоном operator(),

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

struct comparator
{
template<typename T, typename U>
bool operator()(T const& lhs, U const& rhs) const {
return lhs.mCommonField < rhs.mCommonField;
}
};

Тогда это так же просто, как:

std::sort(aStructs.begin(), aStructs.end(), comparator{});
std::sort(bStructs.begin(), bStructs.end(), comparator{});
// ...
std::set_intersection(aStructs.begin(), aStructs.end(),
bStructs.begin(), bStructs.end(),
std::back_inserter(intersection),
comparator{}
);

Просто отметьте, что поскольку в компараторе есть шаблон, он должен быть объявлен вне области действия функции. Живой пример на Coliru Viewer.

3. Ждите C ++ 14

А с добавлением общих лямбд в C ++ 14 вы можете использовать следующее с соответствующим компилятором:

auto cmp = [](auto lhs, auto rhs) { return lhs.mCommonField < rhs.mCommonField; };
// ...
std::sort(aStructs.begin(), aStructs.end(), cmp);
std::sort(bStructs.begin(), bStructs.end(), cmp);
// ...
std::set_intersection(aStructs.begin(), aStructs.end(),
bStructs.begin(), bStructs.end(),
std::back_inserter(intersection),
cmp);

Опять живой пример на Coliru Viewer.


Кроме того, struct typedefs в стиле C не нужны в C ++ (и, возможно, неясны в большинстве мест в C), так что где бы вы ни находились

typedef struct Foo {
// ...
} Foo;

Вы можете заменить его на

struct Foo {
// ...
};

без каких-либо других изменений, необходимых для вашего кода.

12

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

используйте std :: copy_if и lambda, которые ищут внутри vectorB, используя std :: binary_search
не забудьте отсортировать, используя тот же предикат, который вы дадите двоичному

это решает похожую проблему:
элегантный способ удалить все элементы вектора, которые содержатся в другом векторе?

0

set_intersection не волшебство, если у вас есть отсортированные векторы, очень легко свернуть свои собственные, примерно так:

auto ta = aStructs.begin();
auto tb = bStructs.begin();
while( ta != aStructs.end() && tb != bStructs.end() ){
if( ta->mCommonField < tb->mCommonField ){
++ta;
} else if( tb->mCommonField < ta->mCommonField ){
++tb;
} else {
std::cout << "found common: " << ta->mCommonField << std::endl;
++ta;
++tb;
}
}
-2
По вопросам рекламы [email protected]