我需要用std :: set_intersection做一些有点奇怪的事情,我无法弄明白。大约一个月前我问了一个类似的question,并且由于对这个问题的出色回答,我解决了使用两个向量之间的公共链接字段使std :: set_intersection工作的问题,每个向量包含不同的类型对象。
我现在面临的问题是我试图让下面的代码工作,我基本上需要将std :: set_intersection的输出写入一个新类型,它实际上是一些字段之间的联合StructA和StructB中的其他字段。我使用了由用户tclamb编写的略微修改的示例,但它没有编译,我在编译器错误中有点丢失。我很确定我面临的一些问题与
的限制有关根据std::set_intersection中对类型的要求,InputIterator1和InputIterator2具有相同的值类型。在我的情况下,这不是真的,在tclamb解决方案的情况下也不是这样,但它似乎有效。
我刚刚编辑了下面的代码并整合了@ ivar对一些冗余代码的建议 - 这使得问题更容易阅读 - 它现在正在编译和运行 - 但仍然产生的结果不是我想要的。实时代码也发布在coliru
#include<vector>
#include<algorithm>
#include<string>
#include <iostream>
#include <iterator>
// I wish to return a vector of these as the result
struct StructC {
std::string mCommonField;
std::string mNameFromA; // cherry picked from StructA
std::string mNameFromB; // cherry picked from StructB
float mFloatFromA; // cherry picked from StructA
int mIntFromB; // cherry picked from StructB
};
struct StructA {
// conversion operator from StructA to StructC
operator StructC() { return { mCommonField, mNameAString, "[]", mFloatValueA, 0 }; }
std::string mCommonField;
std::string mNameAString;
float mFloatValueA;
};
struct StructB {
// conversion operator from StructB to StructC
operator StructC() { return { mCommonField, "[]", mNameBString, 0.0f, mIntValueB }; }
std::string mCommonField;
std::string mNameBString;
int mIntValueB;
};
// Comparator thanks to @ivar
struct Comparator {
template<typename A, typename B>
bool operator()(const A& a, const B& b) const {
return a.mCommonField < b.mCommonField;
}
};
template<typename CharT, typename Traits>
std::basic_ostream<CharT, Traits>& operator<<(std::basic_ostream<CharT, Traits>& os, StructC const& sc) {
return os << sc.mCommonField << " - " << sc.mNameFromA << " - "
<< sc.mNameFromB << " - " << std::fixed << sc.mFloatFromA << " - " << sc.mIntFromB << std::endl;
}
int main() {
Comparator comparator;
// initially unsorted list of StructA
std::vector<StructA> aStructs = {
{"hello", "nameA1", 1.0f},
{"goodbye", "nameA2", 2.0f},
{"foo", "nameA3", 3.0f}
};
// initially unsorted list of StructB
std::vector<StructB> bStructs = {
{"hello", "nameB1", 10}, // <-- intersection as mCommonField("hello") also in aStructs
{"goodbye", "nameB2", 20}, // <-- intersection as mCommonField("goodbye") also in aStructs
{"bar", "nameB3", 30}
};
// in the above list, "hello" & "goodbye" are the common in both aStructs & bStructs
// pre-sort both sets before calling std::intersection
std::sort(aStructs.begin(), aStructs.end(), comparator);
std::sort(bStructs.begin(), bStructs.end(), comparator);
std::vector<StructC> intersection;
std::set_intersection(aStructs.begin(), aStructs.end(),
bStructs.begin(), bStructs.end(),
std::back_inserter(intersection),
comparator);
std::copy(intersection.begin(), intersection.end(),
std::ostream_iterator<StructC>(std::cout, ""));
return 0;
}
答案 0 :(得分:3)
有两个错误。首先,back_inserter
创建back_insert_iterator<vector<StructC>>
,其operator=
上vector<StructC>::value_type
为StructC
。从StructA
和StructB
到StructC
没有转换,所以我们需要一个。添加一个的最简单方法是
struct StructA {
// ...
operator StructC() { return {mCommonField, int(mFloatValue), 0}; }
};
等。其次,operator <<
没有StructC
。修复这些错误,我们有一个fully functional solution。
UPD。您可以将结果放入vector<Common>
,这看起来非常类似于此问题。
答案 1 :(得分:1)
我有点困惑但你的编辑,所以我希望我能得到最新的问题。
正如我之前在评论中所述,您可以为C
和A
添加B
个构造函数
struct StructA {
std::string mCommonField;
std::string mNameAString;
float mFloatValueA;
};
struct StructB {
std::string mCommonField;
std::string mNameBString;
int mIntValueB;
};
struct StructC {
std::string mCommonField;
std::string mNameFromA = "[]";
std::string mNameFromB = "[]";
float mFloatFromA = 0;
int mIntFromB = 0;
StructC(const StructA& a) : mCommonField{a.mCommonField},
mNameFromA{a.mNameAString}, mFloatFromA{a.mFloatValueA} {}
StructC(const StructB& b) : mCommonField{b.mCommonField},
mNameFromB{b.mNameBString}, mIntFromB{b.mIntValueB} {}
};
而非A
/ B
次转化为C
。我们的想法是C
应该更好地了解如何从部分信息中构建自己。我认为这是您在polkovnikov.ph's answer下面的评论中所关注的问题。此外,使用类内初始化程序,可以将成员初始化为默认值,以便每个构造函数只关心相关内容。
另一个问题是,您并不需要构建Common
个对象来进行比较;这是纯粹的开销。您可以使用通用比较器执行相同的操作:
struct Comparator
{
template<typename A, typename B>
bool operator()(const A& a, const B& b) const
{
return a.mCommonField < b.mCommonField;
}
};
现在不再需要struct Common
(除非你出于其他目的需要它)。
除此之外,我没有看到任何问题。
答案 2 :(得分:1)
我想我现在更清楚你最初在寻找什么:
给定两个输入范围
A
,B
,对于a
中的A
b
,B
中找到a
为了等效,您希望将b
,c
合并到新对象C
中,并将其复制到输出范围std::set_intersection
。
如果我现在这样做,恐怕我无法想到可以实现这一目标的标准算法的组合。 std::set_intersection
根本不是 泛型。
所以我的第二次尝试是通过添加Merger
函数对象来概括template<
class InputIt1, class InputIt2,
class OutputIt, class Compare, class Merge
>
OutputIt set_intersection
(
InputIt1 first1, InputIt1 last1,
InputIt2 first2, InputIt2 last2,
OutputIt d_first, Compare comp, Merge merge
)
{
while (first1 != last1 && first2 != last2)
{
if (comp(*first1, *first2))
++first1;
else
{
if (!comp(*first2, *first1))
*d_first++ = merge(*first1++, *first2);
++first2;
}
}
return d_first;
}
:
Merger
这是struct Merger
{
template<typename A, typename B>
StructC operator()(const A& a, const B& b) const { return {a, b}; }
};
课程的样子:
StructC
以及struct StructC {
std::string mCommonField;
std::string mNameFromA;
std::string mNameFromB;
float mFloatFromA;
int mIntFromB;
StructC(const StructA& a, const StructB& b) : mCommonField{a.mCommonField},
mNameFromA{a.mNameAString}, mNameFromB{b.mNameBString},
mFloatFromA{a.mFloatValueA}, mIntFromB{b.mIntValueB} {}
};
实际处理合并的方式:
goodbye - nameA2 - nameB2 - 2.000000 - 20
hello - nameA1 - nameB1 - 1.000000 - 10
根据需要,live example现在提供
std::set_intesection
注意:{{1}}的代码就是我从cppreference.com复制的代码,这是一个简化的功能等价物。在生产代码中,我会检查与转发,异常,特殊情况,优化以及我没有想到的任何问题相关的实际实现。