【问题标题】:Invalid operator< using std::equal_to无效的运算符<使用 std::equal_to
【发布时间】:2014-02-02 16:04:11
【问题描述】:

不详细说明我为什么要做我正在做的事情,让我描述一下这个问题。

我使用 std::set 来存储名为 VertexTypePos3Normal 的结构的唯一对象。

结构体定义如下:

struct VertexTypePos3Normal {
 // ctor, dtor ..
 friend bool operator==(const VertexTypePos3Normal& v1, const VertexTypePos3Normal& v2);
 friend bool operator<(const VertexTypePos3Normal& v1, const VertexTypePos3Normal& v2);
 glm::vec3 pos;
 glm::vec3 normal;

};

bool operator<(const VertexTypePos3Normal& v1, const VertexTypePos3Normal& v2) {
    return (v1.pos.x < v2.pos.x) && (v1.pos.y < v2.pos.y) && (v1.pos.z < v2.pos.z) && (v1.normal.x < v2.normal.x) && (v1.normal.y < v2.normal.y) && (v1.normal.z < v2.normal.z); 
}

// operator == ommited

默认情况下 std::set 使用 std::less 作为比较函数。 所以我首先将我的集合声明为std::set&lt;VertexTypePos3Normal&gt; set; 插入集合的元素存储在不包含唯一值的 std::vector 中(循环遍历向量)。 使用 std::less 调用了我的操作符

下面是插入集合的方法:

void createUniqueVertices(const std::vector<const VertexTypePos3Normal>& verticesIn,
                                  std::vector<const VertexTypePos3Normal>& verticesOut,
                                  std::vector<unsigned short>& indicesOut)
        {

            //std::map<VertexTypePos3Normal, int, std::equal_to<VertexTypePos3Normal> > map;
            std::set<const VertexTypePos3Normal, std::equal_to<const VertexTypePos3Normal> > set;

            int indexCounter = 0;

            for (auto c_it = verticesIn.cbegin(); c_it != verticesIn.cend(); ++c_it) {

                //bool newlyAdded = map.insert(std::pair<VertexTypePos3Normal, int>(*c_it, indexCounter)).second;

                bool newlyAdded = set.insert(*c_it).second;

                //if (newlyAdded) {
                    //verticesOut.push_back(*c_it);
                    //map.insert(std::pair<VertexTypePos3Normal, int>(*c_it, indexCounter));
                    //++indexCounter;
                //}

                //indicesOut.push_back(map[*c_it]);
            }
        }

所以我正要尝试使用 std::equal_to 而不是 std::less 并写了 operator==。 现在奇怪的事情开始了:

虽然我不再调用 std::less,因此也不再调用 operator

所以实际上我有两个问题:

1.) 为什么我的操作符

2.) operator

编辑:感谢所有信息。看起来我完全误解了严格的弱排序。使用 std::tie 照顾它解决了我的问题。这是更新的代码:

void createUniqueVertices(const std::vector<const VertexTypePos3Normal>& verticesIn,
                                  std::vector<const VertexTypePos3Normal>& verticesOut,
                                  std::vector<unsigned short>& indicesOut)
        {

            std::map<VertexTypePos3Normal, int> map;

            int indexCounter = 0;

            for (auto c_it = verticesIn.cbegin(); c_it != verticesIn.cend(); ++c_it) {

                bool newlyAdded = map.insert(std::pair<VertexTypePos3Normal, int>(*c_it, indexCounter)).second;

                if (newlyAdded) {
                    verticesOut.push_back(*c_it);
                    //map.insert(std::pair<VertexTypePos3Normal, int>(*c_it, indexCounter));
                    ++indexCounter;
                }

                indicesOut.push_back(map[*c_it]);
            }
        }

我在最终版本中使用地图,因为该集合已过时。

这是我的新操作符

bool operator<(const VertexTypePos3Normal& v1, const VertexTypePos3Normal& v2) {
            return (std::tie(v1.pos.x, v1.pos.y, v1.pos.z, v1.normal.x, v1.normal.y, v1.normal.z) < std::tie(v2.pos.x, v2.pos.y, v2.pos.z, v2.normal.x, v2.normal.y, v2.normal.z));
        }

【问题讨论】:

  • Can't reproduce(这是我能够从您提供给我们的少量信息中得出的示例)。
  • 问题在运行时插入到集合中。对不起,如果我提供的信息不够。我添加了插入集合的方法。如果问题现在提供更多信息,请重新投反对票。
  • 断言不一定是由您的operator&lt; 触发的,我认为这是一条通用消息,表明您的比较谓词在某种程度上无效,可能是因为您的operator== 不符合strict weak ordering要求(非自反性)。

标签: c++ stl set operator-overloading


【解决方案1】:

有序关联容器需要strict weak ordering 关系。需要的属性之一是反对称,即cmp(x,y) 意味着!cmp(y,x)。您对operator&lt; 的定义不满足此属性。

此外,相等(或等价)可以定义为!(cmp(x,y)||cmp(y,x)),并且通常使用它来代替x==y。也就是说,operator&lt; 可能会被调用,即使您没有明确使用它。

【讨论】:

  • 首先感谢您。我知道严格的弱排序规则,但看不到任何我不满意的规则。而且我知道 operator
  • 无论如何,使用std::equal_to 可能比你不正确的operator&lt; 还要糟糕。
  • @Chris operator== 被调用是因为您已将比较谓词定义为集合为std::equal_to,您希望它在这种情况下做什么?您尚未向我们展示您的 operator== 是如何定义的,但它很可能违反了 cmp(x,x) == false,这是严格弱排序的要求。
  • std::equal_to 正在调用 operator
  • @Chris 我不明白你为什么一直这么说。您已经通过放置一个永远不会命中的断点来证明它没有被调用。在问题下方的 cmets 中查看我的解释。并在另一个答案下方解决您的评论,std::tie 不在乎您给它什么元素,所以如果您希望 posnormal 都在比较中,只需添加它们。 return std::tie(v1.pos.x, v1.pos.y, v1.pos.z, v1.normal.x, v1.normal.y, v1.normal.z) &lt; std::tie(...);
【解决方案2】:

您的运算符

你可能想要:

bool operator<(const VertexTypePos3Normal& v1, const VertexTypePos3Normal& v2) {
    if(v1.pos.x < v2.pos.x) return true;
    else if(v1.pos.x == v2.pos.x) {
        if(v1.pos.y < v2.pos.y) return true;
        else if(v1.pos.y == v2.pos.y) {
            if(v1.pos.z < v2.pos.z) return true;
            else if(v1.pos.z < v2.pos.z) {
                if(v1.normal.x < v2.normal.x) return true;
                else if(v1.normal.x == v2.normal.x) {
                    if(v1.normal.y < v2.normal.y) return true;
                    else if(v1.normal.y < v2.normal.y) {
                        if(v1.normal.z < v2.normal.z) return true;
                    }
                }
            }
        }
    }
    return false;
}

注意:这应该拆分为 glm::vec3 的两个 less 函数调用(具有 bool less(const glm::vec3&, const glm::vec3&);)

【讨论】:

  • 或者干脆return std::tie(v1.pos.x, v1.pos.y, v1.pos.z) &lt; std::tie(v2.pos.x, v2.pos.y, v2.pos.z);
猜你喜欢
  • 2018-05-16
  • 1970-01-01
  • 2012-01-21
  • 2020-08-21
  • 1970-01-01
  • 1970-01-01
  • 2021-10-21
  • 1970-01-01
  • 2016-05-02
相关资源
最近更新 更多