Что такое std:: векторные руководства по дедукции в C++17?
Я читал о дедукции руководства для std::vector С помощью cppreference.
пример:
#include <vector>
int main() {
std::vector<int> v = {1, 2, 3, 4};
std::vector x{v.begin(), v.end()}; // uses explicit deduction guide
}
Итак, у меня есть несколько вопросов об этом:
каковы
std::vectorруководства по дедукции в C++17?почему и когда нам нужна векторная дедукция?
Здесь
xastd::vector<int>илиstd::vector<std::vector<int>>?
3 ответов
каковы
std::vectorруководства по дедукции в C++17?
определяемое пользователем руководство по вычету позволяет пользователям решать, как вычет аргумента шаблона класса выводит аргументы для класса шаблона из аргументов конструктора. В этом случае, кажется, что std::vector имеет явное руководство, которое должно сделать конструкцию из пары итераторов более интуитивной.
почему и когда нам нужен вектор дедукция?
нам это не "нужно", но это полезно в общем коде и в коде, который очень очевиден (т. е. код, в котором явное указание аргументов шаблона не выгодно для читателя).
и
xavector<int>илиvector<vector<int>>?
вот хороший трюк, чтобы понять это быстро - написать объявление функции шаблона без определения и попытаться вызвать его. Компилятор распечатайте тип передаваемых аргументов. Вот что печатает g++ 8:
template <typename>
void foo();
// ...
foo(x);
ошибка: нет подходящей функции для вызова
foo(std::vector<__gnu_cxx::__normal_iterator<int*, std::vector<int> > ...
как вы можете видеть из сообщения об ошибке,x вывод std::vector<std::vector<int>::iterator>.
почему?
std::vectorдедукция руководства доступны на cppreference.org. Стандарт, похоже, определяет явное руководство по дедукции из итератора пара:
поведение, встречающееся в g++ 8, кажется правильным независимо от того, как (цитирую Rakete1111)
разрешение перегрузки предпочитает конструктор с
std::initializer_listС рамно список инициализаторадругие конструкторы считаются только после того, как все
std::initializer_listконструкторы были опробованы в список-инициализации
std:vector<std::vector<int>::iterator> поэтому правильный результат при использовании инициализации списка. видео
при строительстве x С std::vector x(v.begin(), v.end()), int вместо этого будет выведено. видео
Здесь
xastd::vector<int>илиstd::vector<std::vector<int>>?
другие ответы здесь касаются ваших других вопросов, но я хотел бы рассмотреть этот немного более тщательно. Когда мы делаем вывод аргумента шаблона класса, мы синтезировать кучу шаблонов функций от конструкторов, а затем еще от руководство по дедукции и выполните разрешение перегрузки для определения правильного шаблона параметры.
есть довольно много конструкторов std::vector<T,A> , но большинство из них не упоминаю T что бы T не выводил контекст и, следовательно, не является жизнеспособным вариантом при этом перегрузки. Если мы предварительно обрезаем набор, чтобы использовать только те, которые могут быть жизнеспособными:
template <class T> vector<T> __f(size_t, T const& ); // #2
template <class T> vector<T> __f(vector<T> const& ); // #5
template <class T> vector<T> __f(vector<T>&& ); // #6, NB this is an rvalue ref
template <class T> vector<T> __f(initializer_list<T> ); // #8
а потом еще этот руководство по вычету, который я также упрощу, отбросив распределитель:
template <class InputIt>
vector<typename std::iterator_traits<InputIt>::value_type> __f(InputIt, InputIt );
это наши 5 кандидатов, и мы перегружаемся, как будто [dcl.init], вызов через __f({v.begin(), v.end()}). Поскольку это инициализация списка, мы с initializer_list кандидатов и, только если их нет, мы переходим к другим кандидатам. В этом случае существует initializer_list кандидат, который жизнеспособен (#8), поэтому мы выбираем его, даже не рассматривая какие-либо другие. Этот кандидат выводит T as std::vector<int>::iterator, поэтому мы перезапускаем процесс разрешения перегрузки, чтобы выбрать конструктор для std::vector<std::vector<int>::iterator> list-инициализируется двумя итераторами.
это, вероятно, не желаемый результат - мы, вероятно, хотели vector<int>. Решение Там простое: используйте ()s:
std::vector x(v.begin(), v.end()); // uses explicit deduction guide
теперь мы не делаем инициализацию списка, поэтому initializer_list кандидат не является реальным кандидатом. В результате мы выводим vector<int> через руководство по дедукции (единственный жизнеспособный кандидат) и в конечном итоге вызывает конструктор итераторов-пар. Это имеет побочное преимущество фактически делать комментарий правильный.
это одно из многих мест, где инициализации с {} делает что-то дико отличается от инициализации с (). Некоторые утверждают, что {} является равномерной инициализацией-какие примеры, как это, кажется, противоречат. Мое эмпирическое правило: используйте {} когда вам конкретно, сознательно нужно поведение, которое {} обеспечивает. () иначе.
каковы
std::vectorруководства по дедукции в C++17?
вычет аргумента шаблона класса указывает: "для создания экземпляра шаблона класса должен быть известен каждый аргумент шаблона, но не каждый аргумент шаблона должен быть указан."
и это локализовано для std:vector, Я имею в виду std:vector это просто класс. Ничего особенного.
здесь std::vector руководство deducation от ref:
template< class InputIt,
class Alloc = std::allocator<typename std::iterator_traits<InputIt>::value_type>>
vector(InputIt, InputIt, Alloc = Alloc())
-> vector<typename std::iterator_traits<InputIt>::value_type, Alloc>;
если вы не знакомы с синтаксисом, пожалуйста, прочитайте каковы руководства по вычету шаблонов в C++17?
почему и когда нам нужна векторная дедукция?
вам нужны руководства, когда вывод типа из аргументов не основан на типе одного из этих аргументов.
это x a
vector<int>илиvector<vector<int>>?
ни!
это Ан:
std::vector<std::vector<int>::iterator>
принуждение к простой ошибке компиляции (путем присвоения номера x например) откроет свой тип):
error: no match for 'operator=' (operand types are 'std::vector<__gnu_cxx::__normal_iterator<int*, std::vector<int> >, std::allocator<__gnu_cxx::__normal_iterator<int*, std::vector<int> > > >' and 'int')
PS:
зачем нам нужна эта инициализация, Alloc = Alloc () в этом руководстве?
это аргумент по умолчанию, что позволяет передать в распределитель. Дефолт означает, что вам не нужны два проводника.
