为什么在 GCC 中 std::set 推导失败?

Why does deduction fail for std::set in GCC?

我有一个 std::set 允许从迭代器范围中扣除。

#include <iostream>
#include <set>

int main() 
{
   std::set s1 = {1,2,3,4}; 
   std::set s2(s1.begin(), s1.end());
}

GCC 中的上述程序 failed to compile

这里std::set为什么推演失败?

只需使用当前的 GCC 版本(此时为 8.0.0)即可构建。 std::set 的模板推导指南似乎并未在较旧的 GCC 版本的标准库中实现。

std::set 的迭代器构造函数的演绎指南最近才添加到 gcc HEAD 中。

根据 gcc-mirror/gcc 在 GitHub 的说法,std::set 的迭代器构造函数的推导指南是 added and merged to libstdc++-v3 less than two weeks ago

(Extract from diff for libstdc++-v3/include/bits/stl_set.h)

 +#if __cpp_deduction_guides >= 201606
 +
 +  template<typename _InputIterator,
 +       typename _Compare =
 +         less<typename iterator_traits<_InputIterator>::value_type>,
 +       typename _Allocator =
 +         allocator<typename iterator_traits<_InputIterator>::value_type>,
 +       typename = _RequireInputIter<_InputIterator>,
 +       typename = _RequireAllocator<_Allocator>>
 +    set(_InputIterator, _InputIterator,
 +    _Compare = _Compare(), _Allocator = _Allocator())
 +    -> set<typename iterator_traits<_InputIterator>::value_type,
 +      _Compare, _Allocator>;
 +
 +  template<typename _Key, typename _Compare = less<_Key>,
 +       typename _Allocator = allocator<_Key>,
 +       typename = _RequireAllocator<_Allocator>>
 +    set(initializer_list<_Key>,
 +    _Compare = _Compare(), _Allocator = _Allocator())
 +    -> set<_Key, _Compare, _Allocator>;
 +
 +  template<typename _InputIterator, typename _Allocator,
 +       typename = _RequireInputIter<_InputIterator>,
 +       typename = _RequireAllocator<_Allocator>>
 +    set(_InputIterator, _InputIterator, _Allocator)
 +    -> set<typename iterator_traits<_InputIterator>::value_type,
 +       less<typename iterator_traits<_InputIterator>::value_type>,
 +       _Allocator>;
 +
 +  template<typename _Key, typename _Allocator,
 +       typename = _RequireAllocator<_Allocator>>
 +    set(initializer_list<_Key>, _Allocator)
 +    -> set<_Key, less<_Key>, _Allocator>;
 +
 +#endif

这很自然地解释了为什么模板参数推导对于早期版本的 gcc 的迭代器构造函数的模板化 set 失败,例如7.2.0。如果使用当前的 gcc trunk (gcc HEAD 8.0.0 20171103 (experimental)) the deduction guides above are available,并且迭代器构造函数的模板参数推导也成功。

至于为什么模板参数推导在 gcc 7.2.0 中已经成功用于 std::initializer_list 构造函数(没有推导指南;指南也在上面的提交中添加),正如在 @JohnZwinck deleted answer,这些构造函数本身并没有模板化(不是由它们自己的模板参数列表参数化),而是使用 set 的成员类型 value_type——它只是 [=15= 的一个 typedef ] 的第一个模板类型参数 Key——作为 std::initializer 列表的模板参数,我假设这会产生一个足够简单的推导路径,即使没有明确的推导指南也能成功。