【问题标题】:Unique struct as std::map key not inserting all data作为 std::map 键的唯一结构不插入所有数据
【发布时间】:2020-10-27 21:45:56
【问题描述】:

我正在尝试用“关键数据”填充不同的地图,然后将“关键数据”作为关键数据提取到主地图中。然而;主地图正在跳过几个唯一条目,就好像已经存在一个键一样。

谁能解释一下其余的键发生了什么以及为什么它们没有插入到主地图中。

这是我用作打开主地图的键的结构。 (是的,我知道我可以将 std::tie 用于运算符重载。我的项目约束是 c++98 ):

struct MyStruct
{
    std::string stringVar;
    unsigned unsigVar;
    float floatVar;

    bool operator<(MyStruct const &rhs) const
    {
        return ((this->unsigVar  > rhs.unsigVar)  ||
                (this->stringVar > rhs.stringVar) ||  
                (this->floatVar  > rhs.floatVar));
    }   
};

所有“关键地图”和主地图

std::map< std::string, std::vector<std::string> > stringMap;
std::map< std::string, std::vector<unsigned> > unsigMap;
std::map< std::string, std::vector<float> > floatMap;
std::map<MyStruct, int> masterMap;

获取关键数据并推入向量

std::string keys[3] = {"key1", "key2", "key3"};   

std::vector<std::string> strVector;
std::vector<unsigned> unsigVector;
std::vector<float> floatVector;

strVector.push_back("str1");
strVector.push_back("str2");

unsigVector.push_back(10);
unsigVector.push_back(20);

floatVector.push_back(36.0);
floatVector.push_back(37.0);

将矢量数据存储到“键映射”中

for (int i=0; i < sizeof(keys)/sizeof(keys[0]); i++)
{
   stringMap.insert( std::make_pair(keys[i], strVector) ); 
   unsigMap.insert( std::make_pair(keys[i], unsigVector) );
   floatMap.insert( std::make_pair(keys[i], floatVector) );
}

然后我遍历所有地图并将“关键数据”插入到带有虚假数据的主地图中

// iterators for data maps
std::map< std::string, std::vector<std::string> >::iterator stringIter;
std::map< std::string, std::vector<unsigned> >::iterator unsigIter;
std::map< std::string, std::vector<float> >::iterator floatIter;

// point to first key only
stringIter = stringMap.find(keys[0]);
unsigIter = unsigMap.find(keys[0]);
floatIter = floatMap.find(keys[0]);

// loop through data maps and store into master map
for (int i=0; i < stringIter->second.size(); i++)
{
    for (int j=0; j < unsigIter->second.size(); j++)
    {
        for (int k=0; k < floatIter->second.size(); k++)
        {
            // create struct to easily store into master map
            MyStruct myStruct;
            myStruct.stringVar = stringIter->second[i];
            myStruct.unsigVar = unsigIter->second[j];
            myStruct.floatVar = floatIter->second[k];

            // bogus data
            int data = 3;

            masterMap.insert( std::make_pair(myStruct, data) );

            std::map<MyStruct, int>::iterator masterIter;
            masterIter = masterMap.find(myStruct);

            //make sure all keys & data were inserted
            if (masterIter != masterMap.end())
            {
                std::cout << stringIter->second[i] << "\t"
                          << unsigIter->second[j] << "\t"
                          << floatIter->second[k] << "\t"
                          << masterIter->second <<std::endl;
            }
        }
    }
}

这是我得到的:

str1    10      36      3
str1    10      37      3
str1    20      37      3
str2    20      37      3

这是我期望得到的:

str1    10      36      3
str1    10      37      3
str1    20      36      3 <--- missing
str1    20      37      3
str2    10      36      3 <--- missing
str2    10      37      3 <--- missing
str2    20      36      3 <--- missing
str2    20      37      3

【问题讨论】:

  • 你试过调试你的代码吗?
  • 你确定这是一个有效的严格弱排序吗?乍一看,它不是。如果不是,则结果是违反先决条件,从而“混淆”地图,从而导致未定义的行为。
  • 您的订单已损坏。例如,{“”, 1, 0} &lt; {“”, 0, 1}{“”, 0, 1} &lt; {””, 1, 0} 都是 true,但它们不能都排在另一个之前。
  • 旁注:定义正确的排序关系是一件看起来比实际容易得多的事情。

标签: c++ c++98


【解决方案1】:

您可能打算进行类似这样的比较:

bool operator<(MyStruct const &rhs) const
    {
        return ((unsigVar  < rhs.unsigVar)  ||
                ((unsigVar  == rhs.unsigVar) && 
                (stringVar < rhs.stringVar) ||  
                ((stringVar == rhs.stringVar) && 
                (floatVar  < rhs.floatVar))));
    }   

【讨论】:

    【解决方案2】:

    您的operator&lt; 不满足严格的弱 订购要求。

    通过从operator==,!=,<,<=,>,>=,<=>(std::tuple) 复制std::tuple 的示例实现,您可以使其如下所示。

    实现模式不使用相等比较来允许浮点比较出现在任何地方。即使您的浮点比较在最后,我也保留了该模式,因为它非常易于阅读和扩展。

    它按降序排序,因为它看起来像您在问题中想要的那样:

    bool operator<(MyStruct const &rhs) const {
        if(rhs.unsigVar < unsigVar) return true;
        if(unsigVar < rhs.unsigVar) return false;
    
        // the "unsigVar"s are equal
    
        if(rhs.stringVar < stringVar) return true;
        if(stringVar < rhs.stringVar) return false;
    
        // the "stringVar"s are equal
    
        // the last one only needs one comparison:
        return rhs.floatVar < floatVar;
    } 
    

    【讨论】:

    • &lt; 并不比== 更神奇。使用== 的方法将产生相同的结果。不过,这读起来更整洁。
    • 是的,如果需要其他排序顺序,我认为扩展和重新排列非常简单。不过,我还没有将程序集与使用更紧凑的三元运算符进行比较。我期望优化后的版本是相似的。
    • 您的意思是用嵌套的条件运算符表达式替换整个函数体?组件肯定是相同的,是的,因为语义是相同的。虽然不是很好的代码。
    【解决方案3】:

    你的比较函数不正确,因为这两个比较都是真的:(1, "b")

    • 1
    • 2

    所以只添加了这些键中的一个。

    添加到@steger 的答案,您可以使用元组生成比较函数:

    bool operator<(MyStruct const &rhs) const {
       return std::make_tuple(unsigVar, stringVar, floatVar) < std::make_tuple(
          rhs.unsigVar, rhs.stringVar, rhs.floatVar
       );
    }
    

    元组比较进行词法比较。见https://en.cppreference.com/w/cpp/utility/tuple/operator_cmp中的第三点

    请注意,浮点比较充满了问题:https://www.boost.org/doc/libs/1_63_0/libs/math/doc/html/math_toolkit/float_comparison.html

    【讨论】:

    • 啊,是的。错过了那个约束。
    猜你喜欢
    • 2012-01-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-11-04
    • 2019-12-12
    • 2021-01-01
    • 1970-01-01
    • 2018-11-19
    相关资源
    最近更新 更多