The reason the compiler chooses the second constructor in the case of your A is simple: your 10 is the type value of the signed type int , and size_type is some unsigned integer type. This means that 10 needs to be converted to this unsigned integer type. The need for this conversion is what causes the first constructor to lose the overload resolution to the second constructor (which is an exact match for InputIterator = int ). You can work around this problem by doing
A<int> a(10u, 10);
This eliminates the need for an int -> unsigned conversion and makes the first constructor a gain in overload resolution with the sentence "no template is better than template".
Meanwhile, the reason it works differently with std::vector is because the language specification gives a special relation to standard sequence constructors. It just requires that the call to the std::vector constructor with two integers of the same type as the arguments is somehow "magically" resolved by the first constructor from your quote (i.e., the size-initializer constructor). How each specific implementation is achieved, which depends on the implementation. It can overload the constructor for all integer types. It can use functionality similar to enable_if . It can even hard write it to the compiler itself. And so on.
Here is how it is stated in C ++ 03, for example
23.1.1 Sequences
9 For each sequence defined in this section and in paragraph 21:
- constructor
template <class InputIterator> X(InputIterator f, InputIterator l, const Allocator& a = Allocator())
should have the same effect as:
X(static_cast<typename X::size_type>(f), static_cast<typename X::value_type>(l), a)
if InputIterator is an integral type
C ++ 11 does it even further, approaching it from a different angle, although the intention remains the same: it states that if the InputIterator cannot be qualified as an input iterator, then the template constructor should be excluded from permission overloading.
So, if you want your template A template to behave just like std::vector does, you must intentionally design it that way. You can really look into the standard library implementation on your platform to see how they do it for std::vector .
In any case, a low-tech brute force solution would have to add a dedicated overloaded constructor for the int argument
explicit A(unsigned int size = 0, const T &t = T()) { ... } explicit A(int size = 0, const T &t = T()) { ... }
This may, of course, mean that you end up having to add overloads for all integer types.
The best solution that I mentioned above would be to disable the template constructor for integer arguments using enable_if or a similar technique based on SFINAE. for instance
template <typename InputIterator> A(InputIterator first, InputIterator last, typename std::enable_if<!std::is_integral<InputIterator>::value>::type* = 0)
Do you have C ++ 11 features available to you in your compiler?