Как компилятор различает конструкторы std :: vector? - PullRequest
2 голосов
/ 21 февраля 2020

Класс 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());

Когда создается экземпляр с T=size_t, кажется, что они способны к sh (с 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
}

РЕДАКТИРОВАТЬ: я поместил версию @ jrok также в примере. Почему один из конструкторов имеет приоритет в a1, а другой в a2?

Ответы [ 2 ]

5 голосов
/ 21 февраля 2020

Компилятору требуется при разрешении перегрузки отбрасывать перегрузку шаблона из набора функций-кандидатов, когда InputIt не выводится как тип итератора (более формально, когда он не удовлетворяет концепции LegacyInputIterator - см. note (5) на этой странице ).

Удаление перегрузок в пользовательском коде выполняется с помощью техники SFINAE , хотя реализация компилятора может использовать что-то другое - они не требуется строго использовать легальный код C ++. Например, мой (MinGW) компилятор реализует его с помощью SFINAE:

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

, где _RequireInputIter равно

 template<typename _InIter>
    using _RequireInputIter = typename
      enable_if<is_convertible<typename
        iterator_traits<_InIter>::iterator_category,
                   input_iterator_tag>::value>::type;

Это требование вступило в силу с 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);
}

, теперь он напечатает 3 ... 42.

3 голосов
/ 21 февраля 2020

In [sequence.reqmts] / 3 стандарт имеет

[...] i и j, обозначают итераторы, которые отвечают требованиям Cpp17InputIterator и ссылаются на к элементам, неявно преобразуемым в value_­type [...]

И это требование означает, что

template <class InputIt>
vector(InputIt first, InputIt last, const Allocator& alloc = Allocator());

является жизнеспособным, только если InputIt на самом деле является итератором ввода или лучше , Это означает, что реализация должна использовать SFINAE или другие методы, чтобы эта перегрузка не учитывалась, когда InputIt не является действительным типом итератора. size_t не является типом итератора, поэтому единственный конструктор, который можно использовать для создания объекта, это

vector(size_type count, const T& value, const Allocator& alloc = Allocator());

Это отличается от вашего минимального примера. С вашим минимальным примером вы можете вызвать

A(size_t n, const size_t &v) - non-template-specialization
// or
A(size_t first, size_t last) - template-specialization

И если по правилам [over.ics.ref] / 1 обе функции одинаково хороши, так как const size_t & считается тождественным преобразование, которое означает, что специализация не по шаблону называется , предпочтительнее по отношению к специализациям шаблона

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...