Reputation: 1884
The std::vector<T>
class has the following two constructors:
vector(size_type count, const T& value, const Allocator& alloc = Allocator());
template <class InputIt>
vector(InputIt first, InputIt last, const Allocator& alloc = Allocator());
When instantiated with T=size_t
, these seem to be able to clash (with InputIt=size_t
), but it does not happen. Why?
For clarity, here's a minimal code sample:
#include <iostream>
template <typename T>
struct A {
A(size_t n, const T &v) { std::cout << n << " x " << v << std::endl; }
template <typename I>
A(I first, I last) { std::cout << first << " ... " << last << std::endl; }
};
int main() {
size_t x = 3, y = 42;
A<size_t> a1(x, y); // prints 3 x 42
A<size_t> a2(3, 42); // prints 3 ... 42
}
EDIT: I put @jrok's version also in the example. Why is one of the constructors prioritized in a1
, and the other one in a2
?
Upvotes: 2
Views: 126
Reputation: 180935
In [sequence.reqmts]/3 the standard has
[...]
i
andj
denote iterators that meet the Cpp17InputIterator requirements and refer to elements implicitly convertible tovalue_type
[...]
And this requirement means that
template <class InputIt>
vector(InputIt first, InputIt last, const Allocator& alloc = Allocator());
Is only viable if InputIt
is actually a input iterator or better. This means that the implementation must use SFINAE or other techniques to make this overload not considered when InputIt
is not an actual iterator type. size_t
is not an iterator type so the only constructor that can be used to construct the object is
vector(size_type count, const T& value, const Allocator& alloc = Allocator());
This is different from your minimal example. With your minimal example you could call
A(size_t n, const size_t &v) - non-template-specialization
// or
A(size_t first, size_t last) - template-specialization
And if by the rules of [over.ics.ref]/1 both functions are equally good since const size_t &
is considered the identity transformation which means the non-template-specialization is called as those are preferred to template-specializations
Upvotes: 3
Reputation: 55415
The compiler is required, during overload resolution, to drop the template overload from the set of candidate functions when InputIt
is not deduced as an iterator type (more formally, when it doesn't satisfy LegacyInputIterator
concept - see note (5) on this page).
Removing overloads in user code is done with SFINAE technique, altough the compiler implementaion could use something else - they're not strictly required to use legal C++ code. For example, my (MinGW) compiler implements it with SFINAE:
template<typename _InputIterator,
typename = std::_RequireInputIter<_InputIterator>>
vector(_InputIterator __first, _InputIterator __last,
const allocator_type& __a = allocator_type());
where _RequireInputIter
is
template<typename _InIter>
using _RequireInputIter = typename
enable_if<is_convertible<typename
iterator_traits<_InIter>::iterator_category,
input_iterator_tag>::value>::type;
This requirement came into effect with C++11, so prior to it, you could in fact call the wrong constructor. If we modify your example a little:
#include <iostream>
template <typename T>
struct A {
A(size_t n, const T &v) { std::cout << n << " x " << v << std::endl; }
template <typename I>
A(I first, I last) { std::cout << first << " ... " << last << std::endl; }
};
int main() {
A<size_t> a(3, 42);
}
it now prints 3 ... 42
.
Upvotes: 5