C++17定义存储自定义对象的STL set报静态断言失败
存储自定义对象的std::set正确定义方法
错误根因
该代码在C++17及以上版本编译失败,触发comparison object must be invocable as const静态断言,核心原因如下:
- C++17标准起,STL为set、map等底层基于红黑树实现的关联容器新增了编译期校验:要求传入的比较器必须支持在const限定的实例上被调用
- 你实现的
Cmp比较仿函数中,operator()没有添加const修饰,属于非const成员函数,STL内部以const引用持有比较器对象时,无法调用该非const成员函数,直接触发编译错误 - C11、C14版本未加入该静态校验,因此不规范的写法可以侥幸编译通过,但存在潜在的未定义行为风险。
可行修复方案
方案1:为比较仿函数的operator()添加const修饰
这是改动最小的修复方式,仅需要在比较函数的声明末尾加上const关键字,标明该函数不会修改仿函数自身的成员变量,即可满足const调用要求:
class Cmp{ public: Cmp(){}; // 末尾添加const修饰 bool operator() (const triplets &a, const triplets &b) const { if( a.x == b.x){ return a.y < b.y; }else{ return a.x < b.x; } } };
提示:你当前的比较逻辑仅判断了x、y两个维度,若两个triplets对象x、y值相等但z值不同,会被set判定为重复元素直接去重。如果需要三个维度共同判定元素唯一性,需要把z值的比较逻辑补充到判断分支中,避免误去重。
方案2:为自定义类重载const版本的<运算符
如果该比较逻辑是triplets类的默认排序规则,不需要支持多种排序策略,可以直接在类内部重载<运算符,set默认使用的std::less<triplets>会自动调用该重载完成比较,无需单独传入自定义比较器:
class triplets{ public: int x,y,z; triplets(){} triplets(int x,int y,int z){ this->x=x; this->y=y; this->z=z; } // 重载const版本的<运算符,补全三个维度的比较逻辑 bool operator<(const triplets& b) const { if(x != b.x) return x < b.x; if(y != b.y) return y < b.y; return z < b.z; } }; int main(){ // 不需要传入第二个比较器模板参数 set<triplets> s; s.insert(triplets(2,4,5)); return 0; }
实现注意事项
- 所有供STL关联容器使用的比较规则,必须满足严格弱序要求:不能出现
a < b和b < a同时成立的情况,等价判定需要满足传递性,否则会导致红黑树结构错乱,触发未定义行为 - 无论是自定义仿函数还是重载运算符,涉及比较逻辑的成员函数都建议添加const修饰,兼容STL内部的const调用约定,避免高版本C++标准下出现编译错误。
内容的提问来源于stack exchange,提问作者Akash Dahane
相关产品推荐
相关产品推荐

