无法对具有公共字段的不同类型的结构应用std :: set_intersection

joh*_*co3 6 c++ lambda stl set-intersection c++11

我正在尝试使用std :: set_intersection来查找具有公共绑定"name"字段的2种完全不同类型的数据结构之间的公共元素.

我在这里查看了以下输入链接描述,但它似乎迫使我沿着路线在我试图避免的两种不同结构类型之间进行自定义转换(因为这些类型来自第三方)

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

// 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;
    });
Run Code Online (Sandbox Code Playgroud)

我正在使用Visual Studio 2013来编译上面的内容,但是上面的代码会出现如下所示的大量错误.通过std :: set_intersection读取我在将兼容的StrictWeakOrdering comp last参数放在一起时遇到问题.理想情况下,我希望将其作为一个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);
Run Code Online (Sandbox Code Playgroud)

1> C:\ Program Files(x86)\ Microsoft Visual Studio 12.0\VC\include\algorithm(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\algorithm(3625):请参阅函数模板实例化'_OutIt std :: _Set_intersection <_InIt1,_InIt2,_OutIt,_Pr>(_ InIt1,_InIt1,_InIt2,_InIt2,_OutIt,_Pr)'正在编译1> 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):参见参考资料函数模板实例化'_OutIt std :: _ Set_intersection2(_InIt1,_InIt1,_InIt2,_InIt2,_OutI t,_Pr,std :: true_type)'正在编译1> 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):参见函数模板实例化'_OutIt std :: set_intersection >>,std :: _ Vector_iterator >>,std :: back_insert_iterator >>,main ::>(_ InIt1,_InIt1,_InIt2,_InIt2,_OutIt,_Pr)'正在编译1> 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());
Run Code Online (Sandbox Code Playgroud)

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

1> C:\ Program Files(x86)\ Microsoft Visual Studio 12.0\VC\include\xutility(521):错误C2664:'bool main :: comparator :: operator()(const main :: StructA&,const main: :StructB&)const':不能将参数1从'main :: StructA'转换为'const main :: StructB&'1>原因:无法从'main :: StructA'转换为'const main :: StructB'1>没有可用于执行此转换的用户定义转换运算符,或者无法调用运算符1> C:\ Program Files(x86)\ Microsoft Visual Studio 12.0\VC\include\xutility(625):请参阅函数模板参考实例化'bool std :: _ Debug_lt_pred <_Pr,main :: StructA&,main :: StructA&>(_ Pr,_Ty1,_Ty2,std :: _ Dbfile_t,std :: _ Dbline_t)'正在编译1> 1> [1>
_Pr = main ::比较器1>,_Ty1 = main :: StructA&1>,_Ty2 = main :: StructA&1>] 1> C:\ Program Files(x86)\ Microsoft Visual Studio 12.0\VC\include\xutility(636 ):参见函数模板实例化'void std :: _ Debug_order2 <_InIt,_Pr>(_ FwdIt,_FwdIt,_Pr,std :: _ Dbfile_t,std :: _ Db line_t,std :: forward_iterator_tag)'正在编译1> 1> [1>
_InIt = std :: _ Vector_iterator >> 1>,_Pr = main ::比较器1>,
_FwdIt = std :: _ Vector_iterator >> 1>] 1 > C:\ Program Files(x86)\ Microsoft Visual Studio 12.0\VC\include\algorithm(3649):参见函数模板实例化'void std :: _ Debug_order <_InIt1,_Pr>(_ InIt,_InIt,_Pr,std: :_Dbfile_t,std :: _ Dbline_t)'正在编译1> 1> [1>
_InIt1 = std :: _ Vector_iterator >> 1>,_Pr = main ::比较1>,
_ InIt = std :: _ Vector_iterator >> 1>] 1> ....\src\dlf\main.cpp(118):参见函数模板实例化'_OutIt std :: set_intersection >>,std :: _ Vector_iterator >>,std :: back_insert_iterator >>,main ::比较器>(_ InIt1,_InIt1,_InIt2,_InIt2,_OutIt,_Pr)'正在编译1> 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>] 1> C:\ Program Files(x86)\ Microsoft Visual Studio 12.0\VC\include\xutility(52 3):错误C2664:'bool main :: comparator :: operator()(const main :: StructA&,const main :: StructB&)const':不能将参数1从'main :: StructA'转换为'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 :: comparator :: operator()(const main :: StructA&,const main :: StructB &)const':无法将参数2从'main :: StructB'转换为'const main :: StructA&'1>原因:无法从'main :: StructB'转换为'const main :: StructA'1>无用户 - 可以执行此转换的-defined-conversion操作符,或者无法调用操作符1> C:\ Program Files(x86)\ Microsoft Visual Studio 12.0\VC\include\xutility(625):请参阅函数模板实例化参考' bool std :: _ Debug_lt_pred <_Pr,main :: StructB&,main :: Stru ctB&>(_ Pr,_Ty1,_Ty2,std :: _ Dbfile_t,std :: _ Dbline_t)'正在编译1> 1> [1>
_Pr = main ::比较器1>,_Ty1 = main :: StructB&1>,_Ty2 = main :: StructB&1>] 1> C:\ Program Files(x86)\ Microsoft Visual Studio 12.0\VC\include\xutility(636):参见函数模板实例化'void std :: _ Debug_order2 <_InIt,_Pr >(_ FwdIt,_FwdIt,_Pr,std :: _dbfile_t,std :: _ Dbline_t,std :: forward_iterator_tag)'正在编译1> 1> [1>
_InIt = std :: _ Vector_iterator >> 1>,_ Pr = main ::比较器1>,
_ FwdIt = std :: _ Vector_iterator >> 1>] 1> C:\ Program Files(x86)\ Microsoft Visual Studio 12.0\VC\include\algorithm(3650):参见函数模板实例化'void std: :_Debug_order <_InIt2,_Pr>(_ InIt,_InIt,_Pr,std :: _ Dbfile_t,std :: _ Dbline_t)'正在编译1> 1> [1>
_InIt2 = std :: _ Vector_iterator >> 1>,_ Pr = main: :比较器1>,
_ InIt = std :: _ Vector_iterator >> 1>] 1> C:\ Program Files(x86)\ Microsoft Visual Studio 12.0\VC\include\xutility(523):错误C2664:'bool main :: comparator :: OPERAT 或()(const main :: StructA&,const main :: StructB&)const':不能将参数2从'main :: StructB'转换为'const main :: StructA&'1>原因:无法从'main转换:: StructB'到'const main :: StructA'1>没有可用于执行此转换的用户定义转换运算符,或者无法调用运算符

tcl*_*amb 14

由于C++的表现力,有几种方法可以解决这个问题.以下内容绝不是详尽无遗的清单.

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

如果你连接到使用lambda表达式,定义可以从两个被隐式构建的类型StructAStructB与包装用于比较的字段.这可以允许在比较之前对构造函数中的字段执行额外的逻辑.例如:

struct Common {
    std::string const& mCommonField;
    Common(StructA const& sa) : mCommonField{sa.mCommonField} {};
    Common(StructB const& sb) : mCommonField{sb.mCommonField} {};
};
Run Code Online (Sandbox Code Playgroud)

然后可以编写比较lambda

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

并使用像

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
                      );
Run Code Online (Sandbox Code Playgroud)

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;
    }
};
Run Code Online (Sandbox Code Playgroud)

然后,它就像:

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{}
                      );
Run Code Online (Sandbox Code Playgroud)

请注意,由于比较器中有模板,因此必须在函数范围之外声明.Coliru Viewer上的实例.

3.等待C++ 14

通过添加到C++ 14的通用lambdas,您可以使用以下符合的编译器:

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);
Run Code Online (Sandbox Code Playgroud)

再次,Coliru Viewer上的实例.


此外,在C++中不需要C风格的struct typedef(并且可以说在C中的大多数地方都不清楚),所以你可以在任何地方

typedef struct Foo {
    // ...
} Foo;
Run Code Online (Sandbox Code Playgroud)

你可以用它替换它

struct Foo {
    // ...
};
Run Code Online (Sandbox Code Playgroud)

无需对代码进行任何其他更改.