You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

相同Key不同比较器的std::set insert返回值赋值是否为定义行为?

咱们来一步步拆解你的问题,先整理好你的代码,再逐个解答核心疑问:

首先是你的代码(格式化后):

#include <iostream>
#include <set>
using std::cout;
using std::endl;
using std::set;

struct A {
 int val;
};

struct c_inc {
 bool operator()(const A& first,const A& second) const {
 return first.val > second.val;
 }
};

struct c_dec {
 bool operator()(const A& first,const A& second) const {
 return first.val < second.val;
 }
};

int main() {
 set<A,c_inc> s1;
 set<A,c_dec> s2;
 auto x = s1.insert({1});
 cout << x.first->val << endl;
 x = s2.insert({1}); // 核心疑问点
 x = s2.insert({0});
 cout << x.first->val << endl;
}

先纠正你的关键假设:两种场景下insert的返回类型并不相同

你推测“两种场景下insert的return type相同”是错误的。std::set<A, c_inc>std::set<A, c_dec>完全独立的类模板实例——因为比较器是std::set的模板参数之一,只要模板参数不同,生成的就是不同的类类型。

对应的,它们的嵌套iterator类型也完全不同:

  • s1.insert(...)返回的是std::pair<std::set<A, c_inc>::iterator, bool>
  • s2.insert(...)返回的是std::pair<std::set<A, c_dec>::iterator, bool>

这两个std::pair的第一个元素类型没有任何隐式转换关系,所以整体类型不兼容,标准C++下这段代码应该编译失败。如果你的编译器允许了这个操作,那是编译器的非标准扩展,不属于标准规定的行为。

核心问题解答:这种赋值是否属于定义行为?有没有问题?

如果编译器通过非标准扩展允许了这个赋值操作,那这属于未定义行为——标准C++完全没有规定不同std::set实例(哪怕元素类型相同)的迭代器可以互相转换或赋值。这样的操作可能导致内存访问错误、迭代器失效,或者其他不可预测的运行时行为,绝对不推荐使用。

此行为是标准规定还是实现依赖?

标准C++明确规定:不同模板参数的std::set实例是不同的类型,它们的迭代器类型也彼此独立,不存在合法的隐式转换路径。因此这种赋值操作是标准不允许的,不属于定义行为。如果你的编译器能编译通过,那是编译器的实现扩展,不是标准要求的,换个编译器(比如从GCC换到Clang,或者开启严格标准模式)就会触发编译错误。

正确的写法

如果需要处理两个不同setinsert结果,应该分别声明独立的变量,避免类型不匹配的问题:

auto x1 = s1.insert({1});
cout << x1.first->val << endl;

auto x2 = s2.insert({1});
x2 = s2.insert({0});
cout << x2.first->val << endl;

内容的提问来源于stack exchange,提问作者PYA

火山引擎 最新活动