【问题标题】:Double map insertion problem双图插入问题
【发布时间】:2011-09-07 16:34:01
【问题描述】:

我有一个 stl::map 将键定义为我定义的对象,并且 int.地图的使用如下: 我有一个特定对象的列表,我想计算我有多少相同的对象。所以我将对象插入到地图中。如果对象已经存在于地图中,我会增加它的值(因此是计数器)。该对象定义了所有基本运算符。该对象由 5 个字符串组成。 == 运算符定义为所有 5 个字符串的比较,并且在逻辑上在上下文中是有意义的。问题是运算符

class Example
{
private:
    string one;
    string two;
    string three;
    string four;
    string five;
public:
    inline Example (string a_one,string a_two, string a_four, string a_five) :
        one(a_one),two(a_two),three(a_three),four(a_four),five(a_five)
        {}

    inline bool operator == (const Example& other) const
    {
        if (one == other.one)
        {
            if (two == other.two)
            {
                if (three == other.three)
                {
                    if (four == other.four)
                    {
                        if (five == other.five)
                        {
                            return true;
                        }
                    }
                }
            }
        }
        return false;
    }

    inline bool operator < (const Example& other) const
    {
        if (one < other.one)
        {
            return true;
        }
        else if (two < other.two)
        {
            return true;
        }
        else if (three < other.three)
        {
            return true ;
        }
        else if (four < other.four)
        {
            return true;
        }
        else if (five < other.five)
        {
            return true;
        }
        else
        {
            return false;
        }
    }
}

void CountExample(Example& example,std::map<Example,int>& counters);

void main()
{
    std::map<Example,int> counters;
    std::list<Example> examples = GetExamples();
    //GetExamples defined elsewhere, and initializes examples with a long list of instances of Example
    std::list<Example>::const_iterator Iter;
    for (Iter = examples.begin();Iter != examples.end();Iter++)
    {
        CountExample(*Iter);
    }
    PrintCounters(counters);//PrintCounters defined elsewhere and prints the map to a file
}

void CountExample(Example& example,std::map<Example,int>& counters)
{
    std::map<Example,int>::const_iterator Iter;
    Iter = counters.find(example);
    if (Iter ==counters.end()) //means the specific Example is not in the map
    {
        counters.insert(std::pair<Example,int>(example,1));
    }
    else
    {
        counters[example] += 1;
    {
}

【问题讨论】:

标签: c++ string stl map operator-overloading


【解决方案1】:

如果您有一个相当现代的编译器,则可以用两个 std::tie()'d 元组之间的单个比较来代替比较阶梯:

#include <tuple>
...
bool operator== (const Example& other) const
{
    return std::tie(one, two, three, four, five)
        == std::tie(other.one, other.two, other.three, other.four, other.five);
}
bool operator < (const Example& other) const
{
    return std::tie(one, two, three, four, five)
         < std::tie(other.one, other.two, other.three, other.four, other.five);
}

顺便说一句,使用std::multiset 来计算特定元素存储在关联容器中的次数可能更简单,这将CountExample 简化为单行

void CountExample(const Example& example, std::multiset<Example>& counters)
{
    counters.insert(example);
}

虽然打印变得有点棘手:

void PrintCounters(const std::multiset<Example>& counters)
{
    for(auto i=counters.begin(); i!=counters.end(); i = counters.upper_bound(*i))
            std::cout << *i << ":" << counters.count(*i) << '\n';
}

在ideone上测试:http://ideone.com/uA7ao

【讨论】:

  • 虽然std::tie 可以为结构/类引入字典顺序,但我不认为它解决了相等和弱顺序的问题......例如,因为它是现在构建的,如果operator&lt; 为假,则!(A &lt; B) 为真,但这并不一定意味着 A 大于 B ...它只是意味着 A 不小于 B,但它们仍然可以相等。所以这仍然不排除在弱排序中建立相等性的问题,尽管这是重组代码的好方法。
  • @Jason operator&lt; for tuples 将所有这些都考虑在内。它被定义为(std::get&lt;0&gt;(lhs) &lt; std::get&lt;0&gt;(rhs)) || (!(std::get&lt;0&gt;(rhs) &lt; std::get&lt;0&gt;(lhs)) &amp;&amp; lhstail &lt; rhstail),其中lhstail 是没有第一个元素的lhsrhstail 是没有第一个元素的rhs
【解决方案2】:

要与多个元素进行比较,您比较的每个元素都会有三个结果:小于、大于或等效。您必须考虑所有这些情况。

bool LessThan(const MyClass & left, const MyClass right)
{
    if (left.one < right.one)
        return true;
    else if (right.one < left.one)
        return false;
    // equivalent in one
    if (left.two < right.two)
        return true;
    else if (right.two < left.two)
        return false;
    // equivalent in one and two
        ...
    return false;
}

【讨论】:

  • 没有 if 的 else 的嵌套不是必需的,并且减少了大约 15 行。
  • @Yoav,正如我所说,你有 3 个结果,但一次比较只能产生 2 个。请注意,right &lt; leftleft &gt; right 相同,那么它可能更有意义。
  • @Yoav 进一步阐述你只需要在更重要的元素相等时比较不太重要的元素。当left.x != right.x 比较结束。
【解决方案3】:

您需要为您的输入提供operator&lt;。这写起来可能很乏味,但您可以使用 Boost.Tuple 来简单地完成它 - 这样,元组会处理比较,让您的代码更易于阅读、编写和理解。

#include <boost/tuple/tuple.hpp>
#include <boost/tuple/tuple_comparison.hpp>
#include <string>

struct Object
{
    std::string a;
    std::string b;
    std::string c;
};

bool operator<(const Object& obj1, const Object& obj2)
{
    return (boost::tie(obj1.a, obj1.b, obj1.c) < 
        boost::tie(obj2.a, obj2.b, obj2.c));
}

【讨论】:

  • 不能使用 boost....(实际上我可以,除非我向我的经理证明没有 boost 就没有简单的解决方案)
  • 如果您可以使用 C++11,您可以使用 std::tie... 或者使用嵌套的 std::pairs 也可以做到这一点
【解决方案4】:

编辑:在考虑了更多问题之后,我决定删除我的旧答案,因为它似乎与当前遇到的问题无关。您的 operator&lt; 方法似乎确实满足了严格弱排序的要求,所以我认为问题出在其他地方,所以我将下面的替代解决方案留在下面......

您似乎在为地图创建总顺序时遇到问题,因此您可能希望查看std::unordered_map 作为替代方案,它会直接应用您的operator== 来检测相等性,而不是使用您的operator&lt;对于严格的弱排序...你必须为你的类提供一个散列函数,否则使用基于散列表的 std::unordered_map 容器非常简单。

【讨论】:

  • 这是令人震惊的 - 我确实这么认为(正如您所解释的那样)并添加了您建议的确切行。仍然 - 插入副本
  • 我认为你的意思是当(A&lt;B) (B&lt;A) 都为假时...我认为(A&lt;B)!(A&lt;B) 不能同时为假,除非我真的需要检查我的布尔逻辑。
  • @jason:内部字符串没有排序标准。一个与两个具有不同的含义,我无法真正对示例的数据成员进行排序。另一方面,我认为我从您那里了解到这并不重要,唯一重要的是结果将是一致的。你是这个意思吗?
  • @Yoav R. :是的,我是说你需要做一些事情来确定地图的总顺序,并且需要一个一致的规则来确定对象是否小于、等于大于或大于彼此。如果您说不同的字符串是完全独立的实体,那么您可能只需要比较其中一个字符串而不是所有字符串。
  • 顺便说一句,如果您遍历您的地图并发现重复项没有组合在一起,那么这明确表明存在某种类型的模棱两可元素来建立订单的测试的元素类似于我在对此答案的更新中描述的内容。它基本上意味着在一组情况下 A
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-01-24
  • 1970-01-01
  • 2011-11-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多