c++ - 无法将 std::set_intersection 应用于具有公共(public)字段的不同类型的结构

标签 c++ c++11 lambda stl set-intersection

我正在尝试使用 std::set_intersection 在具有共同绑定(bind)“名称”字段的 2 种完全不同类型的数据结构之间找到共同元素。

我看了以下enter link description here但这似乎迫使我在我试图避免的两种不同结构类型之间进行自定义转换(因为这些类型来自第 3 方)

下面的代码片段显示了我想要实现的目标。

// 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 来编译上面的代码,但是上面的代码会吐出大量错误,如下所示。通读 std::set_intersection 我在组装兼容的 时遇到问题StrictWeakOrdering comp 最后一个论点。理想情况下,我希望将其实现为一次性 lambda。
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\algorithm(3591): error C2664: 'bool (__vectorcall *)(const main::StructA &,const main::StructB &)' : cannot convert argument 1 from 'main::StructB' to 'const main::StructA &' 1>
Reason: cannot convert from 'main::StructB' to 'const main::StructA' 1> No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called 1>
C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include\algorithm(3625) : see reference to function template instantiation '_OutIt std::_Set_intersection<_InIt1,_InIt2,_OutIt,_Pr>(_InIt1,_InIt1,_InIt2,_InIt2,_OutIt,_Pr)' being compiled 1> with 1> [ 1>
_OutIt=std::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\algorithm(3654) : see reference to function template instantiation '_OutIt std::_Set_intersection2(_InIt1,_InIt1,_InIt2,_InIt2,_OutIt,_Pr,std::true_type)' being compiled 1> with 1> [ 1>
_OutIt=std::back_insert_iterator>> 1> , _Pr=main:: 1> , _InIt1=main::StructA * 1> ,
_InIt2=main::StructB * 1> ] 1> ....\src\dlf\main.cpp(111) : see reference to function template instantiation '_OutIt std::set_intersection>>,std::_Vector_iterator>>,std::back_insert_iterator>>,main::>(_InIt1,_InIt1,_InIt2,_InIt2,_OutIt,_Pr)' being compiled 1> with 1> [ 1>
_OutIt=std::back_insert_iterator>> 1> , _Ty=main::StructA 1> ,
_InIt1=std::_Vector_iterator>> 1> ,
_InIt2=std::_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());

这导致以下详细错误输出。我希望避免自定义结构(因为我尝试使用的实际结构来自第 3 方)以将转换器从 StructA 转换为 StructB,反之亦然,有什么办法可以避免这种情况,而只是有一些简单的 lambda实现两个相对简单的结构与一个公共(public)字段之间的简单绑定(bind)?
提前致谢。

1>C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include\xutility(521): error C2664: 'bool main::comparator::operator ()(const main::StructA &,const main::StructB &) const' : cannot convert argument 1 from 'main::StructA' to 'const main::StructB &' 1> Reason: cannot convert from 'main::StructA' to 'const main::StructB' 1> No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called 1> C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include\xutility(625) : see reference to function template instantiation 'bool std::_Debug_lt_pred<_Pr,main::StructA&,main::StructA&>(_Pr,_Ty1,_Ty2,std::_Dbfile_t,std::_Dbline_t)' being compiled 1> with 1> [ 1>
_Pr=main::comparator 1> , _Ty1=main::StructA & 1> , _Ty2=main::StructA & 1> ] 1> C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include\xutility(636) : see reference to function template instantiation 'void std::_Debug_order2<_InIt,_Pr>(_FwdIt,_FwdIt,_Pr,std::_Dbfile_t,std::_Dbline_t,std::forward_iterator_tag)' being compiled 1> with 1> [ 1>
_InIt=std::_Vector_iterator>> 1> , _Pr=main::comparator 1> ,
_FwdIt=std::_Vector_iterator>> 1> ] 1> C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include\algorithm(3649) : see reference to function template instantiation 'void std::_Debug_order<_InIt1,_Pr>(_InIt,_InIt,_Pr,std::_Dbfile_t,std::_Dbline_t)' being compiled 1> with 1> [ 1>
_InIt1=std::_Vector_iterator>> 1> , _Pr=main::comparator 1> ,
_InIt=std::_Vector_iterator>> 1> ] 1> ....\src\dlf\main.cpp(118) : see reference to function template instantiation '_OutIt std::set_intersection>>,std::_Vector_iterator>>,std::back_insert_iterator>>,main::comparator>(_InIt1,_InIt1,_InIt2,_InIt2,_OutIt,_Pr)' being compiled 1> with 1> [ 1>
_OutIt=std::back_insert_iterator>> 1> , _Ty=main::StructA 1> ,
_InIt1=std::_Vector_iterator>> 1> ,
_InIt2=std::_Vector_iterator>> 1> , _Pr=main::comparator 1> ] 1>C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include\xutility(523): error C2664: 'bool main::comparator::operator ()(const main::StructA &,const main::StructB &) const' : cannot convert argument 1 from 'main::StructA' to 'const main::StructB &' 1> Reason: cannot convert from 'main::StructA' to 'const main::StructB' 1> No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called 1>C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include\xutility(521): error C2664: 'bool main::comparator::operator ()(const main::StructA &,const main::StructB &) const' : cannot convert argument 2 from 'main::StructB' to 'const main::StructA &' 1> Reason: cannot convert from 'main::StructB' to 'const main::StructA' 1> No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called 1> C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include\xutility(625) : see reference to function template instantiation 'bool std::_Debug_lt_pred<_Pr,main::StructB&,main::StructB&>(_Pr,_Ty1,_Ty2,std::_Dbfile_t,std::_Dbline_t)' being compiled 1> with 1> [ 1>
_Pr=main::comparator 1> , _Ty1=main::StructB & 1> , _Ty2=main::StructB & 1> ] 1> C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include\xutility(636) : see reference to function template instantiation 'void std::_Debug_order2<_InIt,_Pr>(_FwdIt,_FwdIt,_Pr,std::_Dbfile_t,std::_Dbline_t,std::forward_iterator_tag)' being compiled 1> with 1> [ 1>
_InIt=std::_Vector_iterator>> 1> , _Pr=main::comparator 1> ,
_FwdIt=std::_Vector_iterator>> 1> ] 1> C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include\algorithm(3650) : see reference to function template instantiation 'void std::_Debug_order<_InIt2,_Pr>(_InIt,_InIt,_Pr,std::_Dbfile_t,std::_Dbline_t)' being compiled 1> with 1> [ 1>
_InIt2=std::_Vector_iterator>> 1> , _Pr=main::comparator 1> ,
_InIt=std::_Vector_iterator>> 1> ] 1>C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include\xutility(523): error C2664: 'bool main::comparator::operator ()(const main::StructA &,const main::StructB &) const' : cannot convert argument 2 from 'main::StructB' to 'const main::StructA &' 1> Reason: cannot convert from 'main::StructB' to 'const main::StructA' 1> No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called

最佳答案

由于 C++ 的表现力,有几种方法可以解决这个问题。以下绝不是详尽的 list 。

1. 将两种类型隐式转换为包装结构以进行比较

如果您喜欢使用 lambda,请定义一个可以从 StructA 隐式构造的类型。和 StructB并包装用于比较的字段。这可以允许在比较之前对构造函数中的字段执行附加逻辑。例如:

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

那么你的比较 lambda 可以写成
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() .

不要使用 lambda,而是使用模板 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 中添加了通用 lambda,您可以将以下内容与一致的编译器一起使用:
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 上的实时示例.

此外,C 风格的结构类型定义在 C++ 中是不必要的(并且可以说在 C 中的大多数地方都不清楚),所以无论你有什么地方
typedef struct Foo {
    // ...
} Foo;

你可以用
struct Foo {
    // ...
};

无需对您的代码进行任何其他更改。

关于c++ - 无法将 std::set_intersection 应用于具有公共(public)字段的不同类型的结构,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/22456655/

相关文章:

C++ - 如果抛出异常,是否释放本地对象?

c++ - 你知道 C++ 中基于正则表达式的解析器可以用来解析流吗?

java - 如何使用 Lambda 将字符串数组转换为整数数组?

java - java中的Lambda this引用

C++/boost : how to signal async task completion?

c# - 从 lambda 查询的结果访问关联时出现问题

c++ - 关于 void 指针、类和转换

c++ - 全局模板函数的问题

c++ - std::bind 是否忽略了符合标准的多余参数?

C++11 嵌套 lambda 编译段错误