编译器如何区分 std::vector 的构造函数?

vuk*_*ung 2 c++ templates constructor vector

std::vector<T>班有以下两个构造函数:

vector(size_type count, const T& value, const Allocator& alloc = Allocator());
template <class InputIt>
vector(InputIt first, InputIt last, const Allocator& alloc = Allocator());
Run Code Online (Sandbox Code Playgroud)

当用 实例化时T=size_t,这些似乎能够(与InputIt=size_t)发生冲突,但它不会发生。为什么?

为了清楚起见,这是一个最小的代码示例:

#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
}
Run Code Online (Sandbox Code Playgroud)

编辑:我也在示例中放置了@jrok 的版本。为什么其中一个构造函数优先于a1,而另一个优先于a2

jro*_*rok 5

在重载决议期间,编译器需要在InputIt未推导出为迭代器类型时从候选函数集中删除模板重载(更正式地说,当它不满足LegacyInputIterator概念时 - 请参阅本页上的注释 (5) )。

删除用户代码中的重载是使用SFINAE技术完成的,尽管编译器实现可以使用其他东西 - 它们并不严格要求使用合法的 C++ 代码。例如,我的 (MinGW) 编译器使用 SFINAE 实现它:

template<typename _InputIterator,
           typename = std::_RequireInputIter<_InputIterator>>
    vector(_InputIterator __first, _InputIterator __last,
       const allocator_type& __a = allocator_type());
Run Code Online (Sandbox Code Playgroud)

这里_RequireInputIter

 template<typename _InIter>
    using _RequireInputIter = typename
      enable_if<is_convertible<typename
        iterator_traits<_InIter>::iterator_category,
                   input_iterator_tag>::value>::type;
Run Code Online (Sandbox Code Playgroud)

此要求在 C++11 中生效,因此在此之前,您实际上可能会调用错误的构造函数。如果我们稍微修改您的示例:

#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);
}
Run Code Online (Sandbox Code Playgroud)

它现在打印3 ... 42.