¿Qué son las guías de deducción de plantillas y cuándo debemos usarlas?

87

El estándar C ++ 17 introduce "guías de deducción de plantillas". Supongo que tienen algo que ver con la deducción del argumento de la nueva plantilla para los constructores introducidos en esta versión del estándar, pero aún no he visto una explicación simple, al estilo de las preguntas frecuentes, de qué son y para qué sirven.

  • ¿Qué son las guías de deducción de plantillas en C ++ 17?

  • ¿Por qué (y cuándo) los necesitamos?

  • ¿Cómo los declaro?

Tristan Brindle
fuente
En particular, me interesaría saber si C ++ 17 STL proporciona alguna guía de deducción (por ejemplo, para std :: pair o std :: tuple). ¿Cuál es la lista completa de tipos de plantillas estándar "deducibles" a partir de C ++ 17?
Quuxplusone
Me interesaría saber si algún compilador lo admite. Probé gcc, clang y vc ++. rextester.com/DHPHC32332 No importa , encontré que funciona solo con gc ++ 8.1 C ++ 17 y 2a g ++ -std = c ++ 17 -O2 -Wall -pedantic -pthread main.cpp && ./a.out
Jean-Simon Brochu

Respuestas:

98

Las guías de deducción de plantillas son patrones asociados con una clase de plantilla que le dicen al compilador cómo traducir un conjunto de argumentos de constructor (y sus tipos) en parámetros de plantilla para la clase.

El ejemplo más simple es el de std::vectory su constructor que toma un par de iteradores.

template<typename Iterator>
void func(Iterator first, Iterator last)
{
  vector v(first, last);
}

El compilador necesita averiguar qué vector<T>'s Tserá tipo. Sabemos cuál es la respuesta; Tdebería ser typename std::iterator_traits<Iterator>::value_type. Pero, ¿cómo le decimos al compilador sin tener que escribir vector<typename std::iterator_traits<Iterator>::value_type>?

Utiliza una guía de deducción:

template<typename Iterator> vector(Iterator b, Iterator e) -> 
    vector<typename std::iterator_traits<Iterator>::value_type>;

Esto le dice al compilador que, cuando llame a un vectorconstructor que coincida con ese patrón, deducirá la vectorespecialización usando el código a la derecha de ->.

Necesita guías cuando la deducción del tipo de los argumentos no se basa en el tipo de uno de esos argumentos. Inicializar un vectordesde un initializer_listutiliza explícitamente el vector's T, por lo que no necesita una guía.

El lado izquierdo no necesariamente especifica un constructor real. La forma en que funciona es que, si usa la deducción del constructor de plantilla en un tipo, coincide con los argumentos que pasa contra todas las guías de deducción (los constructores reales de la plantilla principal proporcionan guías implícitas). Si hay una coincidencia, la usa para determinar qué argumentos de plantilla proporcionar al tipo.

Pero una vez que se realiza esa deducción, una vez que el compilador descubre los parámetros de la plantilla para el tipo, la inicialización para el objeto de ese tipo procede como si nada de eso sucediera. Es decir, la guía de deducción seleccionada no tiene que coincidir con el constructor seleccionado.

Esto también significa que puede usar guías con agregados e inicialización agregada:

template<typename T>
struct Thingy
{
  T t;
};

Thingy(const char *) -> Thingy<std::string>;

Thingy thing{"A String"}; //thing.t is a `std::string`.

Por lo tanto, las guías de deducción solo se utilizan para determinar el tipo que se está inicializando. El proceso real de inicialización funciona exactamente como antes, una vez que se ha tomado esa determinación.

Nicol Bolas
fuente
7
Hmm, se me acaba de ocurrir que incluso con la guía, vector v{first, last};no haré lo correcto :(
TC
3
@TC… a menos que lo correcto sea hacer un vector de iteradores. Y std::string{32,'*'}[0] == ' '(para ASCII). Pero todo esto ha sido cierto desde C ++ 11.
Arne Vogel
2
¿Qué sucede con el parámetro de vector de asignación? ¿Qué pasaría si el parámetro del vector del asignador no tuviera un argumento predeterminado? (no se puede deducirlo de InputIterator)
gnzlbg
@NicolBolas: ¿Le importaría explicar los detalles de cómo las guías de deducción implícitas y explícitas pueden funcionar en el contexto de clases parcial o totalmente especializadas (cuyos constructores claramente no necesitan tener tipos de parámetros que coincidan con los de la plantilla principal)? Es difícil encontrar información sobre esto a través de una búsqueda rápida.
user541686
1
@NicolBolas: Ya veo. No tengo claro que la pregunta sea sobre guías de deducción explícita ... Creo que es útil si solo incluye lo que escribió literalmente en este comentario.
user541686