Что такое 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?почему и когда нам нужна векторная дедукция?
Здесь
x
astd::vector<int>
илиstd::vector<std::vector<int>>
?
3 ответов
каковы
std::vector
руководства по дедукции в C++17?
определяемое пользователем руководство по вычету позволяет пользователям решать, как вычет аргумента шаблона класса выводит аргументы для класса шаблона из аргументов конструктора. В этом случае, кажется, что std::vector
имеет явное руководство, которое должно сделать конструкцию из пары итераторов более интуитивной.
почему и когда нам нужен вектор дедукция?
нам это не "нужно", но это полезно в общем коде и в коде, который очень очевиден (т. е. код, в котором явное указание аргументов шаблона не выгодно для читателя).
и
x
avector<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
вместо этого будет выведено. видео
Здесь
x
astd::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 () в этом руководстве?
это аргумент по умолчанию, что позволяет передать в распределитель. Дефолт означает, что вам не нужны два проводника.