Recently, I was asked this question on an interview:
Which Vector constructor will be called in the code below?
#include <iostream> class Iterator { public: Iterator(int &x): ptr_(&x) {} private: int* ptr_ = nullptr; }; template<class T> class Vector { public: Vector(size_t size, T default_value) { std::cout << "Constructor #1 called\n"; } template<class Iterator> Vector(Iterator first, Iterator last) { std::cout << "Constructor #2 called\n"; } }; int main() { auto v = Vector<int>(3, 5); }
The answer is the second constructor, because the types are the same. I do not really understand the reason. Could anybody explain why that happens?
Also, the second part of the question was to use std::enable_if so that the first constructor would be called instead. What is the way to accomplish this?
Regarding the 1st part:
3and5are literals of typeint.In the 1st constructor, the 2nd parameter is using the template argument named
T, whichmain()is specifying explicitly asint, so5can be passed as-is. However,size_tis an implementation-defined unsigned type, thus notint, so an implicit conversion (ie, integer promotion) is required in order to pass3to the 1st parameter.On the other hand, the 2nd constructor uses a template argument named
Iteratorfor both of its input parameters, and that template argument is being deduced asintin this example, so no implicit conversion is needed. Both3and5can be passed as-is, so the 2nd constructor is an exact match forVector<int>(3, 5).If the 2nd constructor had intended to use the
classnamedIteratorinstead for its input parameters, then the template argument on the 2nd constructor is unnecessary and should be removed, eg:In which case, the 1st constructor becomes a better match for
Vector<int>(3, 5). Even though theIteratorclass can be constructed from anintvariable, overload resolution prefers integer promotion over object construction. Also, because3and5are rvalues, and a non-constint&reference can't bind to an rvalue, so theIteratorclass wouldn't be constructable in this example anyway.