Document number: P00433R0
Date: 2016-10-16
Reply-To:
Mike Spertus, Symantec (mike_spertus@symantec.com)
Walter E. Brown (webrown.cpp@gmail.com)
Audience: {Library Evolution, Library, Core} Working Group
National body comments US7 and US14 request analysis of the standard library to determine what changes might be desirable in light of the C++17 adoption of P0091R3 (Template argument deduction for class templates (rev. 6)). In this paper, we perform such an analysis and recommend wording changes for clauses 17 through 23 and also propose some refinements to the wording of P0091R3. If the committee agrees with the approach taken here, we will perform a similar analysis for the remaining library clauses.
There are some areas where the wording around template deduction for constructors in the draft standard seems unclear. In this paper, we make the following assumptions about the core language.
We make these precise in the following wording changes:
Change §13.3.18 [class.template.deduction] as follows:
13.3.1.8 Class Template Deduction [class.template.deduction]Modify the end of §14.9p1 [temp.deduct.guide] as follows:
The overload set consists of:
- For each deduction-guide, a function or function template with the following properties is a candidate:
- The template parameters, if any, and function parameters are those of the deduction-guide.
- The return type is the simple-template-id of the deduction-guide.
- If the constructor is not explicit, the function shall not be a candidate for a non-explicit conversion function.
- For each constructor of the class template designated by the template-name, a function template with the following properties is a candidate unless a function with the same signature derived from a deduction-guide was added to the candidate list as described above:
- The template parameters are the template parameters of the class template followed by the template parameters (including default template arguments) of the constructor, if any.
- The types of the function parameters (including default arguments) are those of the constructor.
- The return type is the class template specialization designated by the template-name and template arguments corresponding to the template parameters obtained from the class template.
- If the deduction-guide was introduced with the keyword explicit, the function shall not be a candidate for a non-explicit conversion function.
- For each deduction-guide, a function or function template with the following properties is a candidate:
- The template parameters, if any, and function parameters are those of the deduction-guide.
- The return type is the simple-template-id of the deduction-guide.
deduction-guide:
explicitopt template-name ( parameter-declaration-clause ) -> simple-template-id ;
Rationale: All of the headers in §18 are listed in “Table 30 — Language support library summary” in §18.1 [support.general]:
Subclause | Header(s) | |
---|---|---|
18.2 | Common definitions | <cstddef> |
18.3 | Implementation properties | <limits> <climits> <cfloat> |
18.4 | Integer types | <cstdint> |
18.5 | Start and termination | <cstdlib> |
18.6 | Dynamic memory management | <new> |
18.7 | Type identification | <typeinfo> |
18.8 | Exception handling | <exception> |
18.9 | Initializer lists | <initializer_list> |
18.10 | Other runtime support | <csignal> <csetjmps> <cstdalign> <cstdarg> <cstdbool> <cstdlib> |
Rationale: All of the headers in §19 are listed in “Table 31 — Diagnostics library summary” in §19.1:
Subclause | Header(s) | |
---|---|---|
19.2 | Exception classes | <stdexcept> |
19.3 | Assertions | <cassert> |
19.4 | Error numbers | <cerrno> |
19.5 | System error support | <system_error> |
None of these define any class templates, so they are unaffected by template constructor deduction.
This header defines the class templates is_error_code_enum, is_error_condition_enum, and specializations of std::hash, all of which have only default constructors.
Rationale: All of the headers in §20 are listed in “Table 32 — General utilities library summary” in §20.1:
Subclause | Header(s) | |
---|---|---|
20.2 | Utilities components | <utility> |
20.3 | Compile-time integer sequences | <utility> |
20.4 | Pairs | <utility> |
20.5 | Tuples | <tuple> |
20.6 | Optional objects | <optional> |
20.7 | Variants | <variant> |
20.8 | Storage for any type | <any> |
20.9 | Fixed-size sequences of bits | <bitset> |
20.10 | Memory | <memory> <cstdlib> |
20.11 | Smart pointers | <memory> |
20.12 | Memory resources | <memory_resource> |
20.13 | Scoped allocators | <scoped_allocator> |
20.14 | Function objects | <functional> |
20.15 | Type traits | <type_traits> |
20.16 | Compile-time rational arithmetic | <ratio> |
20.17 | Time utilities | <chrono> <ctime> |
20.18 | Type indexes | <typeindex> |
20.19 | Execution policies | <execution> |
void swap(pair& p) noexcept(see below);
};
template<class T1, class T2>
pair(T1&&, T2&&) -> pair<see below>;
}
At the end of §20.4.2 [pairs.pair], add the following:
In addition, change §20.4.3p9 [pairs.spec] as follows:template <class T1, class T2> pair(T1&&, T2&&) -> pair<V1, V2>;
Remarks: V1 and V2 are determined as follows: Let Ui be decay_t<Ti> for each Ti. Then each Vi is X& if Ui equals reference_wrapper<X>, otherwise Vi is Ui.
Note: Although the explicit deduction guide obviates the need for make_pair, we do not propose deprecating make_pair at this time.template <class T1, class T2> constexpr pair<V1, V2>decltype(pair(declval<T1&&>(), declval<T2&&>()) make_pair(T1&& x, T2&& y);Returns: pair<V1, V2>(std::forward<T1>(x), std::forward<T2>(y));
where V1 and V2 are determined as follows: Let Ui be decay_t<Ti> for each Ti. Then each Vi is X& if Ui equals reference_wrapper<X>, otherwise Vi is Ui.
[ Example: In place of:return pair<int, double>(5, 3.1415926); // explicit typesa C++ program may containprovide either:return make_pair(5, 3.1415926); // types are deducedorreturn pair(5, 3.1415926); // types implicitly deduced by constructor
void swap(tuple&) noexcept(see below);
};
template<class... UTypes>
tuple(UTypes...) -> tuple<see below>;
}
At the end of §20.5.2.1 [tuples.cnstr], add the following:
In addition, change the start of §20.5.2.4 [tuple.creation as follows:template <class... Types> tuple(Types...) -> tuple<VTypes...>;
Remarks: The VTypes... are determined as follows: Let Ui be decay_t<Ti> for each Ti in Types. Then each Vi in VTypes is X& if Ui equals reference_wrapper<X>, otherwise Vi is Ui.
In the function descriptions that follow, let i be in the range of [0, sizeof...(TTypes)) in order and let Ti be the ith type in a template parameter pack named TTypes; let j be in the range [0, sizeof...(UTypes)) in order and Uj be the jth type in a template parameter pack named UTypes, where indexing is zero-based.template <class... Types> constexpr tuple<VTypes...>decltype(tuple(declval<Types&&>()...)) make_tuple(Types&&... t);Let Ui be decay_t<Ti> for each Ti in Types. Then each Vi in VTypes is X& if Ui equals reference_wrapper<X>, otherwise Vi is Ui. Returns: tuple<VTypes...>(std::forward<Types>(t)...).
T *val; // exposition only }; template<class T> optional(T&& t) -> optional<remove_reference_t<T>>;
allocator a = allocator<int>();
correctly deduces that
a has type allocator<int> as the constructor
template<class T> template<class U> allocator<T>::allocator<T>(const allocator<U> &)
is not deducible.
int *ip = new int();
unique_ptr uip{ip}; // Deduce unique_ptr<int>
Note that the template parameter still needs to be given explicitly for arrays.
int *ip = new int[5];
unique_ptr uip{ip}; // Error. Deduces unique_ptr<int>
unique_ptr aip<int[]>{ip}; // Ok
At the end of the unique_ptr class definition in §20.11.1.2 [unique.ptr.single],
add the following:
Note that analogous changes are necessary for shared_ptr, etc. as they do not include the deleter type as a template parameter.// disable copy from lvalue unique_ptr(const unique_ptr&) = delete; unique_ptr& operator=(const unique_ptr&) = delete; }; template<class T> unique_ptr(T*) -> unique_ptr<T, default_delete<T>; template<class T, class V> unique_ptr(T*, V) -> unique_ptr<T, default_delete<T, V>; // If V::pointer is not valid template<class U, class V> unique_ptr(U, V) -> unique_ptr<pointer_traits<V::pointer>element_type, V>; // If V::pointer is valid }
scoped_allocator_adaptor select_on_container_copy_construction() const;
};
template<class OuterAlloc, class... InnerAllocs> scoped_allocator_adaptor(OuterAlloc&&, const InnerAllocs&& -> scoped_allocator_adaptor<remove_reference_t<OuterAlloc>, remove_reference_t<InnerAllocs>...>;
If a different outer allocator is desired, then
it can still be specified explicitly.
template<class T> const T* target() const noexcept;
};
template<class R, class... ArgTypes>
function(R(*)(ArgTypes...)) -> function<R(ArgTypes...)>;
template<class R, class C, class... ArgTypes>
function(R(C::*)(ArgTypes...)) -> function<R(C &, ArgTypes...)>;
template<class R, class C, class... ArgTypes>
function(R(C::*)(ArgTypes...) const) -> function<R(C const &, ArgTypes...)>;
template<class R, class C, class... ArgTypes>
function(R(C::*)(ArgTypes...) volatile) -> function<R(C volatile &, ArgTypes...)>;
template<class R, class C, class... ArgTypes>
function(R(C::*)(ArgTypes...) const volatile) -> function<R(C const volatile &, ArgTypes...)>
template<class R, class C> function(R C::*)) ->
function<R&(C &)>
template<class R, class C>
function(const R C::*)) -> function<const R&(C const &)>
template<class R, class C>
function(volatile R C::*)) -> function<volatile R&(volatile C &)>
template<class R, class C>
function(const volatile R C::*)) -> function<const volatile R&(const volatile C &)>
Note: There are some arbitrary choices in the deductions for member pointers. We
would be interested to see if the comittee agrees with having deduction guides for member
pointers in addition to function pointers, and if so, whether these are the right choices.
Since template constructor deduction can be used to construct objects of type default_searcher, boyer_moore_searcher,
and boyer_moore_horspool_searcher, we propose getting rid of the searcher creation functions by
modifying §20.14p2 as follows:
Delete §20.14.13.1.1 [func.searchers.default.creation]// 20.14.13 searchers: template<class ForwardIterator, class BinaryPredicate = equal_to<>> class default_searcher; template<class RandomAccessIterator, class Hash = hash<typename iterator_traits<RandomAccessIterator>::value_type>, class BinaryPredicate = equal_to<>> class boyer_moore_searcher; template<class RandomAccessIterator, class Hash = hash<typename iterator_traits<RandomAccessIterator>::value_type>, class BinaryPredicate = equal_to<>> class boyer_moore_horspool_searcher; template<class ForwardIterator, class BinaryPredicate = equal_to<>> default_searcher<ForwardIterator, BinaryPredicate> make_default_searcher(ForwardIterator pat_first, ForwardIterator pat_last, BinaryPredicate pred = BinaryPredicate()); template<class RandomAccessIterator, class Hash = hash<typename iterator_traits<RandomAccessIterator>::value_type>, class BinaryPredicate = equal_to<>> boyer_moore_searcher<RandomAccessIterator, Hash, BinaryPredicate> make_boyer_moore_searcher( RandomAccessIterator pat_first, RandomAccessIterator pat_last, Hash hf = Hash(), BinaryPredicate pred = BinaryPredicate()); template<class RandomAccessIterator, class Hash = hash<typename iterator_traits<RandomAccessIterator>::value_type>, class BinaryPredicate = equal_to<>> boyer_moore_searcher<RandomAccessIterator, Hash, BinaryPredicate> make_boyer_moore_horspool_searcher( RandomAccessIterator pat_first, RandomAccessIterator pat_last, Hash hf = Hash(), BinaryPredicate pred = BinaryPredicate()); // 20.14.14, hash function primary template:
2014.13.2.1 boyer_moore_searcher creation functions [func.searcher.boyer_moore.creation]Delete §20.14.13.2.1 [func.searchers.boyer_moore.creation]template<class ForwardIterator, class BinaryPredicate = equal_to<>> default_searcher<ForwardIterator, BinaryPredicate> make_default_searcher(ForwardIterator pat_first, RandomAccessIterator pat_last, BinaryPredicate pred = BinaryPredicate());
Effects: Equivalent to:return default_searcher<ForwardIterator, BinaryPredicate>(pat_first, pat_last, pred);
2014.13.2.1 boyer_moore_searcher creation functions [func.searcher.boyer_moore.creation]Delete §20.14.13.3.1 [func.searchers.boyer_moore_horspool.creation]template<class RandomAccessIterator, class Hash = hash<typename iterator_traits<RandomAccessIterator>::value_type>, class BinaryPredicate = equal_to<>> boyer_moore_searcher<RandomAccessIterator, Hash, BinaryPredicate> make_boyer_moore_searcher( RandomAccessIterator pat_first, RandomAccessIterator pat_last, Hash hf = Hash(), BinaryPredicate pred = BinaryPredicate());
Effects: Equivalent to:return boyer_moore_searcher<RandomAccessIterator, Hash, BinaryPredicate>( pat_first, pat_last, hf, pred);
2014.13.2.1 boyer_moore_searcher creation functions [func.searcher.boyer_moore.creation]template<class RandomAccessIterator, class Hash = hash<typename iterator_traits<RandomAccessIterator>::value_type>, class BinaryPredicate = equal_to<>> boyer_moore_horspool_searcher<RandomAccessIterator, Hash, BinaryPredicate> make_boyer_moore_horspool_searcher( RandomAccessIterator pat_first, RandomAccessIterator pat_last, Hash hf = Hash(), BinaryPredicate pred = BinaryPredicate());
Effects: Equivalent to:return boyer_moore_searcher<RandomAccessIterator, Hash, BinaryPredicate>( pat_first, pat_last, hf, pred);
basic_string<charT, traits, Allocator>::basic_string(const charT* str, const Allocator& a = Allocator());
using the equivalent but non-deducible type value_type in place of charT
as follows:
basic_string<charT, traits, Allocator>::basic_string(const value_type* str, const Allocator& a = Allocator());
The vendor could maintain this convention while continuing to satisfy the “as-if” rule in C++17
by adding the following deduction-guide
template<class charT, class traits = char_traits<charT>, class Allocator = allocator<charT>>
basic_string(const charT*, const Allocator& = Allocator()) -> basic_string<charT, Allocator>;
Similar considerations apply to basic_string_view.
In addition to the above explicit deduction guide, wstring_convert and wbuffer_convert (§22.3.3.2.3) benefit from additional implicit deduction guides.size_t cvtcount; // exposition only }; template<class Codecvt,> wstring_convert(Codecvt *, typename Codecvt::state_type) -> wstring_convert<Codecvt, std::allocator<wchar_t>, std::allocator<char>>;
Subclause | Header(s) | |
---|---|---|
23.2 | Requirements | |
23.3 | Sequence containers | <array> <deque> <forward_list> <list> <vector> |
23.4 | Associative containers | <map> <set> |
23.5 | Unordered associative containers | <unordered_map> <unordered_set> |
23.6 | Container adaptors | <queue> <stack> |
For every sequence container defined in this clause and in clause 21:
- If the constructor
is called with a type InputIterator that does not qualify as an input iterator, then the constructor shall not participate in overload resolution.template <class InputIterator> X(InputIterator first, InputInterator last, const allocator_type& alloc = allocator_type())- If the deduction-guide
is called with a type InputIterator that does not qualify as an input iterator, then the deduction-guide shall not participate in overload resolution.template <class InputIterator, class Allocator = allocator<typename InputIterator::value_type>> X(InputIterator, InputIterator, const Allocator& = Allocator()) -> X<typename InputIterator::value_type, Allocator>;
- If the member functions of the forms:
void clear() noexcept;
};
template <class InputIterator, class Allocator = allocator<typename InputIterator::value_type>>
deque(InputIterator, InputIterator,
const Allocator& = Allocator())
-> deque<typename InputIterator::value_type, Allocator>;
At the end of the definition of class forward_list in §23.3.9.1 [forwardlist.overview],
add the following deduction-guide:
void reverse() noexcept;
};
template <class InputIterator, class Allocator = allocator<typename InputIterator::value_type>>
forward_list(InputIterator, InputIterator,
const Allocator& = Allocator())
-> forward_list<typename InputIterator::value_type, Allocator>;
At the end of the definition of class list in §23.3.10.1 [list.overview],
add the following deduction-guides:
void reverse() noexcept;
};
template <class InputIterator, class Allocator = allocator<typename InputIterator::value_type>>
list(InputIterator first, InputIterator last,
const Allocator& = Allocator())
-> list<typename InputIterator::value_type, Allocator>;
At the end of the definition of class vector in §23.3.11.1 [vector.overview],
add the following deduction-guide:
void clear() noexcept;
};
template <class InputIterator, class Allocator = allocator<typename InputIterator::value_type>>
vector(InputIterator, InputIterator,
const Allocator& = Allocator())
-> vector<typename InputIterator::value_type, Allocator>;
Note that this always deduces const keys. We would like feedback from the committee as to whether this is the right choice. At the end of the definition of class multimap in §23.4.5.1 [multimap.overview], add the following deduction-guides:template <class K> pair<const_iterator, const_iterator> equal_range(const K& x) const; }; template <class InputIterator, class Compare = default_order_t<typename InputIterator::value_type::first_type>, class Allocator = allocator<pair<typename InputIterator::value_type>> map(InputIterator, InputIterator, const Compare& = Compare(), const Allocator& = Allocator()) -> map<typename InputIterator::value_type::first_type, typename InputIterator::value_type::second_type, Compare, Allocator>; template <class InputIterator, class Allocator = allocator<pair<typename InputIterator::value_type>> map(InputIterator, InputIterator, const Allocator& = Allocator()) -> map<typename InputIterator::value_type::first_type, typename InputIterator::value_type::second_type, default_order_t<typename InputIterator::value_type::first_type>, Allocator>;
At the end of the definition of class set in §23.4.6.1 [set.overview], add the following deduction-guide:template <class K> pair<const_iterator, const_iterator> equal_range(const K& x) const; }; template <class InputIterator, class Compare = default_order_t<typename InputIterator::value_type::first_type>, class Allocator = allocator<pair<typename InputIterator::value_type>> multimap(InputIterator, InputIterator, const Compare& = Compare(), const Allocator& = Allocator()) -> multimap<typename InputIterator::value_type::first_type, typename InputIterator::value_type::second_type, Compare, Allocator>; template <class InputIterator, class Allocator = allocator<pair<typename InputIterator::value_type>> multimap(InputIterator, InputIterator, const Allocator& = Allocator()) -> multimap<typename InputIterator::value_type::first_type, typename InputIterator::value_type::second_type, default_order_t<typename InputIterator::value_type::first_type>, Allocator>;
At the end of the definition of class multiset in §23.4.7.1 [multiset.overview], add the following deduction-guide:template <class K> pair<const_iterator, const_iterator> equal_range(const K& x) const; }; template <class InputIterator, class Compare = default_order_t<typename InputIterator::value_type>, class Allocator = allocator<pair<typename InputIterator::value_type>> set(InputIterator, InputIterator, const Compare& = Compare(), const Allocator& = Allocator()) -> set<typename InputIterator::value_type, Compare, Allocator>; template <class InputIterator, class Allocator = allocator<pair<typename InputIterator::value_type>> set(InputIterator, InputIterator, const Allocator& = Allocator()) -> set<typename InputIterator::value_type, default_order_t<typename InputIterator::value_type>, Allocator>;
template <class K> pair<const_iterator, const_iterator> equal_range(const K& x) const; }; template <class InputIterator, class Compare = default_order_t<typename InputIterator::value_type>, class Allocator = allocator<pair<typename InputIterator::value_type>> multiset(InputIterator, InputIterator, const Compare& = Compare(), const Allocator& = Allocator()) -> multiset<typename InputIterator::value_type, Compare, Allocator>; template <class InputIterator, class Allocator = allocator<pair<typename InputIterator::value_type>> multiset(InputIterator, InputIterator, const Allocator& = Allocator()) -> multiset<typename InputIterator::value_type, default_order_t<typename InputIterator::value_type>, Allocator>;
Note that this always deduces const keys. We would like feedback from the committee as to whether this is the right choice. At the end of the definition of class unordered_multimap in §23.5.5.1 [unord.multimap.overview], add the following deduction-guide:svoid reserve(size_type n); }; template <class InputIterator, class Hash = hash<typename InputIterator::value_type::first_type>, class Pred = std::equal_to<typename InputIterator::value_type::first_type>, class Allocator = allocator<pair<typename InputIterator::value_type>> unordered_map(InputIterator, InputIterator, size_type = see_below, const Hash& = Hash(), const Pred& = Pred(), const Allocator& = Allocator()) -> unordered_map<typename InputIterator::value_type::first_type, typename InputIterator::value_type::second_type, Hash, Pred, Allocator>; template <class InputIterator, class Allocator = allocator<pair<typename InputIterator::value_type>> unordered_map(InputIterator, InputIterator, size_type, const Allocator& = Allocator()) -> unordered_map<typename InputIterator::value_type::first_type, typename InputIterator::value_type::second_type, hash<typename InputIterator::value_type::first_type>, std::equal_to<typename InputIterator::value_type::first_type>, Allocator>; template <class InputIterator, class Hash = hash<typename InputIterator::value_type::first_type>, class Allocator = allocator<pair<typename InputIterator::value_type>> unordered_map(InputIterator, InputIterator, size_type, const Hash& = Hash(), const Allocator& = Allocator()) -> unordered_map<typename InputIterator::value_type::first_type, typename InputIterator::value_type::second_type, Hash, std::equal_to<typename InputIterator::value_type::first_type>, Allocator>;
At the end of the definition of class unordered_set in §23.5.6.1 [unord.set.overview], add the following deduction-guides:void reserve(size_type n); }; template <class InputIterator, class Hash = hash<typename InputIterator::value_type::first_type>, class Pred = std::equal_to<typename InputIterator::value_type::first_type>, class Allocator = allocator<pair<typename InputIterator::value_type>> unordered_multimap(InputIterator, InputIterator, size_type = see_below, const Hash& = Hash(), const Pred& = Pred(), const Allocator& = Allocator()) -> unordered_multimap<typename InputIterator::value_type::first_type, typename InputIterator::value_type::second_type, Hash, Pred, Allocator>; template <class InputIterator, class Allocator = allocator<pair<typename InputIterator::value_type>> unordered_multimap(InputIterator, InputIterator, size_type, const Allocator& = Allocator()) -> unordered_multimap<typename InputIterator::value_type::first_type, typename InputIterator::value_type::second_type, hash<typename InputIterator::value_type::first_type>, std::equal_to<typename InputIterator::value_type::first_type>, Allocator>; template <class InputIterator, class Hash = hash<typename InputIterator::value_type::first_type>, class Allocator = allocator<pair<typename InputIterator::value_type>> unordered_multimap(InputIterator, InputIterator, size_type, const Hash& = Hash(), const Allocator& = Allocator()) -> unordered_multimap<typename InputIterator::value_type::first_type, typename InputIterator::value_type::second_type, Hash, std::equal_to<typename InputIterator::value_type::first_type>, Allocator>;
At the end of the definition of class unordered_multiset in §23.5.7.1 [unord.multiset.overview], add the following deduction-guides:void reserve(size_type n); }; template <class InputIterator, class Hash = hash<typename InputIterator::value_type>, class Pred = std::equal_to<typename InputIterator::value_type>, class Allocator = allocator<pair<typename InputIterator::value_type>> unordered_set(InputIterator, InputIterator, size_type = see_below, const Hash& = Hash(), const Pred& = Pred(), const Allocator& = Allocator()) -> unordered_set<typename InputIterator::value_type::first_type, Hash, Pred, Allocator>; template <class InputIterator, class Allocator = allocator<pair<typename InputIterator::value_type>> unordered_set(InputIterator, InputIterator, size_type, const Allocator& = Allocator()) -> unordered_set<typename InputIterator::value_type, hash<typename InputIterator::value_type>, std::equal_to<typename InputIterator::value_type>, Allocator>; template <class InputIterator, class Hash = hash<typename InputIterator::value_type>, class Allocator = allocator<pair<typename InputIterator::value_type>> unordered_set(InputIterator, InputIterator, size_type, const Hash& = Hash(), const Allocator& = Allocator()) -> unordered_set<typename InputIterator::value_type, Hash, std::equal_to<typename InputIterator::value_type>, Allocator>;
void reserve(size_type n); }; template <class InputIterator, class Hash = hash<typename InputIterator::value_type>, class Pred = std::equal_to<typename InputIterator::value_type>, class Allocator = allocator<pair<typename InputIterator::value_type>> unordered_multiset(InputIterator, InputIterator, size_type = see_below, const Hash& = Hash(), const Pred& = Pred(), const Allocator& = Allocator()) -> unordered_multiset<typename InputIterator::value_type::first_type, Hash, Pred, Allocator>; template <class InputIterator, class Allocator = allocator<pair<typename InputIterator::value_type>> unordered_multiset(InputIterator, InputIterator, size_type, const Allocator& = Allocator()) -> unordered_multiset<typename InputIterator::value_type, hash<typename InputIterator::value_type>, std::equal_to<typename InputIterator::value_type>, Allocator>; template <class InputIterator, class Hash = hash<typename InputIterator::value_type>, class Allocator = allocator<pair<typename InputIterator::value_type>> unordered_multiset(InputIterator, InputIterator, size_type, const Hash& = Hash(), const Allocator& = Allocator()) -> unordered_multiset<typename InputIterator::value_type, Hash, std::equal_to<typename InputIterator::value_type>, Allocator>;
Note: If a library implementor uses value_type as a constructor parameter type in place of T, they can support template deduction for class templates “as-if” they were using T through explicit deduction guides along the lines of the example in §21 above.void swap(priority_queue& q) noexcept(is_nothrow_swappable_v<Container> && is_nothrow_swappable_v<Compare>) { using std::swap; swap(c, q.c); swap(comp, q.comp); } }; template <class InputIterator, class Container, class Compare> priority_queue(InputIterator, InputIterator, const Compare&, const Container&) -> priority_queue<typename InputIterator::value_type, Container, Compare>; template <class InputIterator, class Container = vector<T>, class Compare = default_order_t<typename Container::value_type>> priority_queue(InputIterator, InputIterator, const Compare& = Compare(), const Container&& = Container()) -> priority_queue<typename InputIterator::value_type, Container, Compare>;