带 enable_if 的过载解决方案

问题描述 投票:0回答:2

最近,我在采访中被问到这个问题:

下面的代码将调用哪个 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
以便调用第一个构造函数。有什么方法可以做到这一点?

c++ sfinae overload-resolution enable-if
2个回答
2
投票

关于第一部分:

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
类在这个例子中无论如何都不是可构造的。


0
投票

要完成第 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";
     }
© www.soinside.com 2019 - 2024. All rights reserved.