为什么 std::flat_set 和 std::flat_map 具有 std::initializer_list 的重载构造函数,而其他容器适配器则没有?

问题描述 投票:0回答:2

我注意到 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
的重载构造函数,而其他容器适配器则没有?

c++ stl initializer-list c++23 stdstack
2个回答
2
投票

我将在这里复制我的标准提案答案,因为它涵盖的内容比上面 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
演员,因为我 假设我们都知道
queue<int> q = {1,2,3}
应该做什么:项目从 队列的前面,所以“1”会在前面,对吗? 我比较怀疑
stack
。我认为没有人会比 50/50
stack<int> st = {1,2,3}
应该做什么,如所写。项目弹出自 堆栈的“顶部”,是的,但是那是左端还是右端? (专家知道这一定是正确的结局,因为这是唯一有效的 当容器是向量时结束;但我不认为那很糟糕 显而易见。)然而,另一方面,迭代器对确实如此 ctor 做了正确的事:如果你按 1,然后按 2,然后按 3,你最终会得到 包含
{1,2,3}
的基础向量。那么为什么不让程序员 首先写
{1,2,3}
?所以我持怀疑态度,但并不完全反对。

无论如何,在所有这些情况下,添加新的角色都会改变过载集 - 并彻底改变它们,因为

initializer_list
演员是偶数 比其他非显式参与者更贪婪。 (这就是为什么隐式转换 都是魔鬼,而STL的流行风格是“让一切变得隐晦” 除非有明确的理由”是错误的默认值 照常。 Python 是对的。)所以这可能就是 LEWG 持怀疑态度的原因 这样做。

OTOH、

flat_set
flat_map
是完全新颖的类别类型;没有人 是否有任何现有代码会因摆弄其超载而被破坏 套。 并且
flat_set
应该是
set
的直接替代品!所以 显然,如果你能写的话,那将是不可能的

std::set<int> s = {1,2,3};

但不是

std::flat_set<int> s = {1,2,3};

这只是工作,期间。


2
投票

我回答我的问题是为了给那些对此主题感兴趣的人提供完整的答案。

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
容器适配器不会出现此问题,因为从一开始就已经提供了重载构造函数,因此不存在出现兼容性问题的风险。

© www.soinside.com 2019 - 2024. All rights reserved.