相同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,或者开启严格标准模式)就会触发编译错误。
正确的写法
如果需要处理两个不同set的insert结果,应该分别声明独立的变量,避免类型不匹配的问题:
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




