generic - template class t c++ example
¿Qué son las guías de deducción de std:: vector en C++ 17? (3)
¿Qué son las guías de deducción de
std::vector
en C ++ 17?
Una guía de deducción definida por el usuario les permite a los usuarios decidir cómo deducir los argumentos de la plantilla de clase deduce los argumentos de una clase de plantilla de sus argumentos de constructor. En este caso, parece que std::vector
tiene una guía explícita que debería hacer que la construcción a partir de un par de iteradores sea más intuitiva.
¿Por qué y cuándo necesitamos la deducción vectorial?
No lo "necesitamos", pero es útil en el código genérico y en el código que es muy obvio (es decir, el código donde especificar explícitamente los argumentos de la plantilla no es beneficioso para el lector) .
¿Es
x
unvector<int>
o unvector<vector<int>>
?
Este es un buen truco para resolver esto rápidamente: escriba una declaración de función de plantilla sin una definición e intente llamarla. El compilador imprimirá el tipo de los argumentos pasados. Esto es lo que g ++ 8 imprime:
template <typename>
void foo();
// ...
foo(x);
error: no hay función coincidente para llamar a
foo(std::vector<__gnu_cxx::__normal_iterator<int*, std::vector<int> > ...
Como puede ver en el mensaje de error, x
se deduce a std::vector<std::vector<int>::iterator>
.
¿Por qué?
std::vector
guías de deducción de std::vector
cppreference . El estándar parece definir una guía de deducción explícita de un par de iteradores:
El comportamiento encontrado en g ++ 8 parece ser correcto independientemente, como (citando Rakete1111 )
la resolución de sobrecarga prefiere el constructor con
std::initializer_list
con la lista de inicializadores reforzadosotros constructores se consideran solo después de que todos los constructores
std::initializer_list
se hayan probado en list-initialization
std:vector<std::vector<int>::iterator>
es, por lo tanto, el resultado correcto cuando se utiliza la inicialización de lista. ejemplo vivo
Al construir x
con std::vector x(v.begin(), v.end())
, se deducirá int
su lugar. ejemplo vivo
Leí acerca de las guías de deducción para std::vector
usando cppreference .
Ejemplo:
#include <vector>
int main() {
std::vector<int> v = {1, 2, 3, 4};
std::vector x{v.begin(), v.end()}; // uses explicit deduction guide
}
Entonces, tengo algunas preguntas al respecto:
¿Qué son las guías de deducción de
std::vector
en C ++ 17?¿Por qué y cuándo necesitamos la deducción vectorial?
Aquí, ¿Es
x
unstd::vector<int>
o unstd::vector<std::vector<int>>
?
¿Qué son las guías de deducción de
std::vector
en C ++ 17?
La deducción de argumento de plantilla de clase especifica: "Para crear una instancia de una plantilla de clase, se deben conocer todos los argumentos de plantilla, pero no se deben especificar todos los argumentos de plantilla".
Y eso está localizado para std:vector
, me refiero a que std:vector
es solo una clase. Nada especial al respecto.
Aquí está la guía de deducción de std::vector
de la referencia:
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>;
Si no está familiarizado con la sintaxis, lea ¿Qué son las guías de deducción de plantillas en C ++ 17?
¿Por qué y cuándo necesitamos la deducción vectorial?
Necesita guías cuando la deducción del tipo de los argumentos no se basa en el tipo de uno de esos argumentos.
¿Es xa
vector<int>
o unvector<vector<int>>
?
¡Ninguno!
Se trata de un:
std::vector<std::vector<int>::iterator>
Forzar un simple error de compilación (asignando un número a x
por ejemplo) revelará su tipo):
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'')
PD:
¿Por qué necesitamos esa inicialización, Alloc = Alloc () en esa guía?
Ese es un argumento por defecto , que permite pasar en un asignador. El valor predeterminado significa que no necesita dos guías.
Aquí, ¿Es
x
unstd::vector<int>
o unstd::vector<std::vector<int>>
?
Las otras respuestas aquí responden a sus otras preguntas, pero quería abordar esto un poco más a fondo. Cuando hacemos deducción de argumentos de plantilla de clase, sintetizamos un montón de plantillas de función de los constructores, y luego algunas más de las guías de deducción y realizamos la resolución de sobrecarga para determinar los parámetros de plantilla correctos.
Existen bastantes constructores para std::vector<T,A>
, pero la mayoría de ellos no mencionan T
lo que haría de T
un contexto no deducido y, por lo tanto, no es una opción viable en esta sobrecarga. Si hacemos un pre-podado del conjunto para usar solo los que podrían ser viables:
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
Y luego también esta cppreference , que también simplificaré al eliminar el asignador:
template <class InputIt>
vector<typename std::iterator_traits<InputIt>::value_type> __f(InputIt, InputIt );
Esos son nuestros 5 candidatos, y estamos sobrecargados como por [dcl.init], una llamada a través de __f({v.begin(), v.end()})
. Debido a que esto es inicialización de lista, comenzamos con los candidatos initializer_list
y, solo si no hay ninguno, procedemos con los otros candidatos. En este caso, hay un candidato initializer_list
que es viable (# 8), por lo que lo seleccionamos sin considerar ninguno de los otros. Ese candidato deduce T
como std::vector<int>::iterator
, por lo que luego reiniciamos el proceso de resolución de sobrecarga para seleccionar un constructor para std::vector<std::vector<int>::iterator>
list-initialized con dos iteradores
Probablemente este no sea el resultado deseado, probablemente queríamos un vector<int>
. La solución es simple: use ()
s:
std::vector x(v.begin(), v.end()); // uses explicit deduction guide
Ahora, no estamos haciendo la inicialización de listas, por lo que el candidato initializer_list
no es un candidato viable. Como resultado, deducimos el vector<int>
través de la guía de deducción (el único candidato viable) y terminamos llamando al constructor del par iterador. Esto tiene el beneficio secundario de hacer que el comentario sea correcto.
Este es uno de los muchos lugares donde la inicialización con {}
hace algo muy diferente a la inicialización con ()
. Algunos sostienen que {}
es una inicialización uniforme, que ejemplos como este parecen contrarrestar. Mi regla de oro: use {}
cuando específicamente, necesita conscientemente el comportamiento que proporciona {}
. ()
contrario.