我注意到 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 :
Yes, any change to overload sets (particularly the addition of new non-explicit constructors) can break code. But that's not necessarily a proposal-killer. For example, there was nothing wrong with C++23’s adopting [P1425] "Iterator-pair constructors for stack and queue" with no change to Annex C, despite its breaking code like this:
void zero(queue<int>); void zero(pair<int*,int*>); int a[10]; void test() { zero({a, a+10}); }
Before: Calls
zero(pair<int, int>)
.
After P1425: Ambiguous.
To fix: Eliminate the ambiguous overloading, or cast the argument topair
.We can simply agree that such examples are sufficiently unlikely in practice, and sufficiently easy to fix, that the benefits of the changed overload set outweigh the costs of running into these examples.
然后我写道:
I think
priority_queue
should get aninitializer_list
ctor, because we all know whatpriority_queue<int> pq = {1,2,3}
ought to do. I thinkqueue
probably should get aninitializer_list
ctor, because I assume we all know whatqueue<int> q = {1,2,3}
ought to do: items pop from the front of the queue, so "1" would be at the front, right? I'm more skeptical ofstack
. I don't think anyone would guess better than 50/50 whatstack<int> st = {1,2,3}
ought to do, as written. Items pop from the "top" of a stack, yes, but is that the left end or the right end? (Experts know it must be the right end because that's the only efficient end when the container is a vector; but I don't think that's terribly obvious.) However, on the other hand, it's true that the iterator-pair ctor Does The Right Thing: if you push 1, then 2, then 3, you end up with an underlying vector containing{1,2,3}
. So why not just let the programmer write{1,2,3}
in the first place? So I'm skeptical, but not completely anti.Anyway, in all of those cases, adding new ctors will change overload sets — and change them drastically, because
initializer_list
ctors are even greedier than other non-explicit ctors. (This is why implicit conversions are the devil, and the STL's prevailing style of "make everything implicit unless there's a positive reason to make it explicit" is the Wrong Default as usual. Python got it right.) So that's probably why LEWG has been leery of doing so.OTOH,
flat_set
andflat_map
are completely novel class types; nobody has any existing code that would be broken by fiddling with their overload sets. Andflat_set
is supposed to be a drop-in replacement forset
! So obviously it would be a non-starter if you could writestd::set<int> s = {1,2,3};
but not
std::flat_set<int> s = {1,2,3};
That just has to work, period.
关于c++ - 为什么 std::flat_set 和 std::flat_map 具有 std::initializer_list 的重载构造函数,而其他容器适配器则没有?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/75678325/