我注意到 C++23 为
std::stack
和 std::queue
容器适配器的构造函数添加了新的重载,允许使用 [first, last)
范围的内容构造底层容器。 cppreference 还展示了如何将这些重载与 std::initializer_list
一起使用,并提供以下示例:
const auto il = {2, 7, 1, 8, 2};
std::stack<int> c4 { il.begin(), il.end() }; // overloads (6), C++23
const auto il = {2, 7, 1, 8, 2};
std::queue<int> c4 { il.begin(), il.end() }; // overload (6), C++23
这意味着不需要引入其他重载构造函数来构造具有
std::initialization_list
内容的底层容器。
然而,C++23还添加了其他容器适配器,例如std::flat_set
,cppreference显示了std::flat_set
容器适配器的实现示例,其中提供了以下重载构造函数:
flat_set(initializer_list<key_type> il, const key_compare& comp = key_compare())
: flat_set(il.begin(), il.end(), comp) { }
template<class Allocator>
flat_set(initializer_list<key_type> il, const key_compare& comp, const Allocator& a);
template<class Allocator>
flat_set(initializer_list<key_type> il, const Allocator& a);
为什么
std::flat_set
和 std::flat_map
具有 std::initializer_list
的重载构造函数,而其他容器适配器则没有?
我将在这里复制我的标准提案答案,因为它涵盖的内容比上面 LoS 自己的答案要多一些。
首先来自P2447R3的报价:
是的,任何对重载集的更改(特别是添加新的非显式构造函数)都可能会破坏代码。但这并不一定是提案杀手。例如,C++23 采用 [P1425]“堆栈和队列的迭代器对构造函数” 并没有更改 附录 C,没有任何问题,尽管它破坏了如下代码:
void zero(queue<int>); void zero(pair<int*,int*>); int a[10]; void test() { zero({a, a+10}); }
之前:打电话
。zero(pair<int, int>)
P1425之后:模棱两可。
修复方法:消除不明确的重载,或将参数转换为。pair
我们可以简单地同意,此类示例在实践中不太可能出现,并且很容易修复,更改后的重载集的好处超过了遇到这些示例的成本。
然后我写道:
我认为
应该找一个priority_queue
演员,因为我们 都知道initializer_list
应该做什么。 我认为priority_queue<int> pq = {1,2,3}
可能应该得到一个queue
演员,因为我 假设我们都知道initializer_list
应该做什么:项目从 队列的前面,所以“1”会在前面,对吗? 我比较怀疑queue<int> q = {1,2,3}
。我认为没有人会比 50/50stack
应该做什么,如所写。项目弹出自 堆栈的“顶部”,是的,但是那是左端还是右端? (专家知道这一定是正确的结局,因为这是唯一有效的 当容器是向量时结束;但我不认为那很糟糕 显而易见。)然而,另一方面,迭代器对确实如此 ctor 做了正确的事:如果你按 1,然后按 2,然后按 3,你最终会得到 包含stack<int> st = {1,2,3}
的基础向量。那么为什么不让程序员 首先写{1,2,3}
?所以我持怀疑态度,但并不完全反对。{1,2,3}
无论如何,在所有这些情况下,添加新的角色都会改变过载集 - 并彻底改变它们,因为
演员是偶数 比其他非显式参与者更贪婪。 (这就是为什么隐式转换 都是魔鬼,而STL的流行风格是“让一切变得隐晦” 除非有明确的理由”是错误的默认值 照常。 Python 是对的。)所以这可能就是 LEWG 持怀疑态度的原因 这样做。initializer_list
OTOH、
和flat_set
是完全新颖的类别类型;没有人 是否有任何现有代码会因摆弄其超载而被破坏 套。 并且flat_map
应该是flat_set
的直接替代品!所以 显然,如果你能写的话,那将是不可能的set
std::set<int> s = {1,2,3};
但不是
std::flat_set<int> s = {1,2,3};
这只是有工作,期间。
我回答我的问题是为了给那些对此主题感兴趣的人提供完整的答案。
在
Std-proposals中提出引入
std::initializer_list
的重载构造函数后,发现将std::initializer_list
的重载构造函数添加到std::stack
和std::queue
容器适配器中是非常困难的没有破坏现有代码的严重风险。
一个例子如下:
Obj* p, q;
[...]
std::stack s{p, q};
如果引入
std::initializer_list
的重载构造函数,这可能会导致问题,因为它可能会生成使用两个指针构造的 std::deque<Obj*>
底层容器,而不是使用元素 std::deque<Obj>
范围内的元素构造的 [p, q)
。
对于 std::flat_set
和 std::flat_map
容器适配器不会出现此问题,因为从一开始就已经提供了重载构造函数,因此不存在出现兼容性问题的风险。