最近,我在采访中被问到这个问题:
下面的代码将调用哪个 Vector 构造函数?
#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); }
答案是第二个构造函数,因为类型是一样的。我不太明白原因。谁能解释为什么会这样?
此外,问题的第二部分是使用
std::enable_if
以便调用第一个构造函数。有什么方法可以做到这一点?
关于第一部分:
3
和 5
是 int
. 类型的文字
在第一个构造函数中,第二个参数使用名为
T
的模板参数,main()
明确指定为int
,因此5
可以按原样传递。但是,size_t
是实现定义的无符号类型,因此不是 int
,因此需要隐式转换(即整数提升)才能将 3
传递给第一个参数。
另一方面,第二个构造函数为其两个输入参数使用名为
Iterator
的模板参数,并且在本示例中该模板参数被 deduced 为 int
,因此不需要隐式转换。 3
和 5
都可以按原样传递,因此第二个构造函数是 Vector<int>(3, 5)
的精确匹配。
如果第二个构造函数打算使用名为
class
的Iterator
代替它的输入参数,那么第二个构造函数的模板参数是不必要的,应该被删除,例如:
template<class T>
class Vector {
public:
Vector(size_t size, T default_value) {
std::cout << "Constructor #1 called\n";
}
Vector(Iterator first, Iterator last) {
std::cout << "Constructor #2 called\n";
}
};
在这种情况下,第一个构造函数成为 better 匹配
Vector<int>(3, 5)
。即使 Iterator
类可以从 int
变量构造,重载决策更喜欢整数提升而不是对象构造。此外,因为 3
和 5
是右值,并且非常量 int&
引用不能绑定到右值,所以 Iterator
类在这个例子中无论如何都不是可构造的。
要完成第 2 部分,按以下方式重写第二个构造函数就足够了:
template<typename Iterator,
typename = std::enable_if_t< !std::is_same_v <Iterator, int> >>
Vector(Iterator first, Iterator last) {
std::cout << "Constructor #2 called\n";
}