Dedução e inspeção de tipos com�auto e decltype
1
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
Dedução automática de tipos
A partir de C++11, especificadores de tipos podem ser omitidos em declarações de objetos, quando estas são acompanhadas de uma expressão inicializadora. Para isso, basta utilizar o "curinga" auto.
2
Qual o significado de auto em C++98/03?
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
Dedução automática de tipos
A partir de C++11, especificadores de tipos podem ser omitidos em declarações de objetos, quando estas são acompanhadas de uma expressão inicializadora. Para isso, basta utilizar o "curinga" auto.
Não confunda auto com inferência dinâmica. O tipo "automático" é deduzido a partir de um valor e não pode ser alterado posteriormente. Em essência, auto é apenas um placeholder para um tipo.
3
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
Simplificando declarações
De fato, substituir int por auto tem benefício questionável. Mas quando os nomes de tipos são longos, compostos ou qualificados, a opção auto é quase irresistível (e elimina chances de typos).
4
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
Simplificando declarações
De fato, substituir int por auto tem benefício questionável. Mas quando os nomes de tipos são longos, compostos ou qualificados, a opção auto é quase irresistível (e elimina chances de typos).
Principalmente no caso de templates, como acontece nos contêineres da STL e seus iteradores.
5
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
Expressividade e portabilidade
Além de reduzir a digitação, auto contribui com a expressividade do código.
6
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
Expressividade e portabilidade
Além de reduzir a digitação, auto contribui com a expressividade do código.
E com a portabilidade também.
7
Qual o tipo de retorno de size()?
x
x
Qual o tamanho de short, int, long, etc.?
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
Decorando auto
8
auto ≡ int
auto ≡ int
auto ≡ int
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
Decorando auto
9
auto ≡ int
auto ≡ int
auto ≡ int
auto ≡ const int
auto ≡ int
auto ≡ int
auto ≡ int
auto ≡ int
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
Decorando auto
10
auto ≡ int
auto ≡ int
auto ≡ int
auto ≡ const int
auto ≡ int
auto ≡ int
auto ≡ int
auto ≡ int
auto&& ≡ const int&
auto ≡ int
auto&& ≡ int&
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
Algoritmo de dedução de auto
As regras de dedução de auto parecem confusas. É verdade... Mas já as conhecemos: são exatamente as mesmas daquelas empregadas em em templates de função.
Conversão de lvalues e rvalues, eliminação de consts e desestruturação de referências se aplicam igualmente.
11
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
Algoritmo de dedução de auto
As regras de dedução de auto parecem confusas. É verdade... Mas já as conhecemos: são exatamente as mesmas daquelas empregadas em em templates de função.
Essa correspondência entre auto e templates de função é definida oficialmente no padrão.
Conversão de lvalues e rvalues, eliminação de consts e desestruturação de referências se aplicam igualmente.
12
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
Quando auto é a única opção
1) Se não houver capturas, é possível converter o lambda em um ponteiro de função. Lambdas ainda serão estudados em detalhes.
As vezes, auto deixa de ser uma conveniência... Ele é o único1 especificador capaz de armazenar lambdas, pois o tipo da função é interno ao compilador.
13
Qual classe da biblioteca pode ser usada com esse propósito?
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
Quando auto é a única opção
As closures definidas pelo compilador são exclusivas a um lambda. Mesmo que dois lambdas possuam estruturas idênticas, seus autos são distintos.
1) Se não houver capturas, é possível converter o lambda em um ponteiro de função. Lambdas ainda serão estudados em detalhes.
As vezes, auto deixa de ser uma conveniência... Ele é o único1 especificador capaz de armazenar lambdas, pois o tipo da função é interno ao compilador.
14
O que é exatamente uma closure?
Qual classe da biblioteca pode ser usada com esse propósito?
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
Críticas a auto: detecção tardia de erro
Se o tipo deduzido por auto não corresponde àquele esperado, o erro é detectado tardiamente.�Há desperdício de computação (na compilação) e desconforto de navegar até o ponto da declaração.
Nota: Essa situação pode ser (parcialmente) evitada se seguirmos a boa prática de postergar, ao máximo, a definição de objetos. Assim, o local do erro é mais preciso.
15
Há outra vantagem em definir um objeto próximo ao seu uso?
Erro apontado
Causa do erro
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
Críticas a auto: ofuscação
auto facilita a declaração de objetos. Mas essa conveniência também tem consequências negativas. A principal crítica à adoção de auto é de que a ausência do tipo explícito na declaração prejudica a inteligibilidade do programa. Além disso, o tipo deduzido por auto nem sempre é o esperado.
Quando a inicialização é via sintaxe {}, auto deduz o tipo como std::initialiazer_list.
16
Qual a política ideal para adoção de auto?
Depender dos insights de IDE seria uma boa opção?
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
Coding guidelines e auto - Qt
Devido a essa dualidade na percepção das vantagens/desvantagens de auto, sua aceitação não é homogênea. Tipicamente, projetos definem em seus coding guidelines quando e como usar auto.
17
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
Coding guidelines e auto - Qt
Devido a essa dualidade na percepção das vantagens/desvantagens de auto, sua aceitação não é homogênea. Tipicamente, projetos definem em seus coding guidelines quando e como usar auto.
18
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
Coding guidelines e auto - LLVM
Devido a essa dualidade na percepção das vantagens/desvantagens de auto, sua aceitação não é homogênea. Tipicamente, projetos definem em seus coding guidelines quando e como usar auto.
19
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
Coding guidelines e auto - Google
Devido a essa dualidade na percepção das vantagens/desvantagens de auto, sua aceitação não é homogênea. Tipicamente, projetos definem em seus coding guidelines quando e como usar auto.
20
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
Coding guidelines e auto - Unreal
Devido a essa dualidade na percepção das vantagens/desvantagens de auto, sua aceitação não é homogênea. Tipicamente, projetos definem em seus coding guidelines quando e como usar auto.
21
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
Coding guidelines e auto - ISO C++
Devido a essa dualidade na percepção das vantagens/desvantagens de auto, sua aceitação não é homogênea. Tipicamente, projetos definem em seus coding guidelines quando e como usar auto.
22
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
Inspecionando tipos com decltype
Enquanto que auto deduz um tipo a partir de uma expressão de inicialização, decltype, também introduzido em C++11, funciona como um mecanismo de inspeção de tipos a partir de expressões arbitrárias.
23
Qual a extensão "decltype" de C++98/03?
Há uma sobreposição (parcial) de funcionalidade entre os dois. Apesar de não-usual, nada nos impede de especificar o tipo de um objeto via inspeção do tipo de um segundo objeto.
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
Inspecionando tipos com decltype
Enquanto que auto deduz um tipo a partir de uma expressão de inicialização, decltype, também introduzido em C++11, funciona como um mecanismo de inspeção de tipos a partir de expressões arbitrárias.
24
Qual a extensão "decltype" de C++98/03?
Há uma sobreposição (parcial) de funcionalidade entre os dois. Apesar de não-usual, nada nos impede de especificar o tipo de um objeto via inspeção do tipo de um segundo objeto.
Como decltype recebe a expressão como argumento, a inicialização não é necessária.
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
Inspecionando tipos com decltype
Enquanto que auto deduz um tipo a partir de uma expressão de inicialização, decltype, também introduzido em C++11, funciona como um mecanismo de inspeção de tipos a partir de expressões arbitrárias.
25
Qual a extensão "decltype" de C++98/03?
Há uma sobreposição (parcial) de funcionalidade entre os dois. Apesar de não-usual, nada nos impede de especificar o tipo de um objeto via inspeção do tipo de um segundo objeto.
Além disso, decltype nos oferece maior controle sobre o tipo da declaração.
auto ≡ double
decltype(a) ≡ int
Como decltype recebe a expressão como argumento, a inicialização não é necessária.
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
Motivação principal
Passar a decltype um objeto de tipo conhecido não é vantagem alguma. A motivação para sua existência vem da programação genérica, onde é comum especificarmos tipos a partir de expressões.
26
Qual biblioteca de C++ é desenvolvida sob esse paradigma?
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
Motivação principal
Passar a decltype um objeto de tipo conhecido não é vantagem alguma. A motivação para sua existência vem da programação genérica, onde é comum especificarmos tipos a partir de expressões.
27
Qual biblioteca de C++ é desenvolvida sob esse paradigma?
x
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
Motivação principal
Passar a decltype um objeto de tipo conhecido não é vantagem alguma. A motivação para sua existência vem da programação genérica, onde é comum especificarmos tipos a partir de expressões.
28
Qual biblioteca de C++ é desenvolvida sob esse paradigma?
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
Motivação principal
Passar a decltype um objeto de tipo conhecido não é vantagem alguma. A motivação para sua existência vem da programação genérica, onde é comum especificarmos tipos a partir de expressões.
29
Qual biblioteca de C++ é desenvolvida sob esse paradigma?
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
Motivação principal
Passar a decltype um objeto de tipo conhecido não é vantagem alguma. A motivação para sua existência vem da programação genérica, onde é comum especificarmos tipos a partir de expressões.
30
Qual biblioteca de C++ é desenvolvida sob esse paradigma?
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
Duas pequenas novidades
31
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
Revisitando a motivação principal
De volta ao exemplo... Aprendemos que, em C++11, auto, isoladamente no retorno, não funciona.
32
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
Revisitando a motivação principal
De volta ao exemplo... Aprendemos que, em C++11, auto, isoladamente no retorno, não funciona.
Porém, C++14 é mais flexível: agora funciona.
33
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
Revisitando a motivação principal
De volta ao exemplo... Aprendemos que, em C++11, auto, isoladamente no retorno, não funciona.
Porém, C++14 é mais flexível: agora funciona.
Mas resta ainda uma lacuna a ser preenchida: o tipo deduzido pode não ser o que desejamos.
34
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
Revisitando a motivação principal
De volta ao exemplo... Aprendemos que, em C++11, auto, isoladamente no retorno, não funciona.
Porém, C++14 é mais flexível: agora funciona.
Mas resta ainda uma lacuna a ser preenchida: o tipo deduzido pode não ser o que desejamos.
35
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
Revisitando a motivação principal
De volta ao exemplo... Aprendemos que, em C++11, auto, isoladamente no retorno, não funciona.
Porém, C++14 é mais flexível: agora funciona.
Mas resta ainda uma lacuna a ser preenchida: o tipo deduzido pode não ser o que desejamos.
36
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
Revisitando a motivação principal
De volta ao exemplo... Aprendemos que, em C++11, auto, isoladamente no retorno, não funciona.
Porém, C++14 é mais flexível: agora funciona.
Mas resta ainda uma lacuna a ser preenchida: o tipo deduzido pode não ser o que desejamos.
37
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
Revisitando a motivação principal
De volta ao exemplo... Aprendemos que, em C++11, auto, isoladamente no retorno, não funciona.
Porém, C++14 é mais flexível: agora funciona.
Mas resta ainda uma lacuna a ser preenchida: o tipo deduzido pode não ser o que desejamos.
Perdemos parte do tipo. O retorno do operator[] é int&, mas com a dedução de auto passa a ser int. No entanto, referências de lvalue não se vinculam a rvalue. Daí, o erro de compilação.
38
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
Revisitando a motivação principal
De volta ao exemplo... Aprendemos que, em C++11, auto, isoladamente no retorno, não funciona.
Porém, C++14 é mais flexível: agora funciona.
Mas resta ainda uma lacuna a ser preenchida: o tipo deduzido pode não ser o que desejamos.
Perdemos parte do tipo. O retorno do operator[] é int&, mas com a dedução de auto passa a ser int. No entanto, referências de lvalue não se vinculam a rvalue. Daí, o erro de compilação.
Solução 1: Abrimos mão da habilidade de alterar o contêiner e não utilizamos uma referência.
39
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
Combinando auto e decltype
Solução 2: Modificamos o retorno para um "novo" especificador que combina auto e decltype.
40
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
Combinando auto e decltype
Solução 2: Modificamos o retorno para um "novo" especificador que combina auto e decltype.
Introduzido em C++14, a ideia de decltype(auto) é basicamente a seguinte:
41
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
Combinando auto e decltype
Solução 2: Modificamos o retorno para um "novo" especificador que combina auto e decltype.
Introduzido em C++14, a ideia de decltype(auto) é basicamente a seguinte:
Como decltype "inspeciona" o tipo de c[i], o retorno é agora é o desejado: int&.
42
Qual seria a expressão contextual no caso de declaração de variável?
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
Peculiaridades de decltype
Assim como auto, decltype também tem suas peculiaridades. Quando seu argumento é uma expressão nomeada, o tipo resultante é composto exatamente pelos mesmos especificadores utilizados na declaração do nome ao qual se refere a expressão.
43
decltype(s) ≡ const char* const
decltype(n) ≡ const int&
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
Peculiaridades de decltype
Assim como auto, decltype também tem suas peculiaridades. Quando seu argumento é uma expressão nomeada, o tipo resultante é composto exatamente pelos mesmos especificadores utilizados na declaração do nome ao qual se refere a expressão.
Mas perante uma expressão anônima, não existe uma declaração à qual decltype possa se referir. Nesse caso, a regra para resolução envolve o tipo base e a categoria da expressão.
44
decltype(s) ≡ const char* const
decltype(n) ≡ const int&
decltype(val()) ≡ int
decltype(10) ≡ int
decltype(val() + 3.14) ≡ double
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
decltype: a surpresa
À primeira vista, tudo parece óbvio. Mas as "surpresas" ainda estão por vir...
45
decltype(ci) ≡ const int
decltype((ci)) ≡ const int&
decltype(((ci))) ≡ const int&
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
decltype: a surpresa
À primeira vista, tudo parece óbvio. Mas as "surpresas" ainda estão por vir...
Não faremos uma discussão rigorosa (requereria conhecimento detalhado sobre as categorias de expressões), mas a seguinte tabela, baseada em lvalues e rvalues, é suficiente para nossos fins:
46
Expressão anônima, e, de tipo base T | Tipo de decltype(e) |
lvalue | T& |
rvalue "em transferência" | T&& |
rvalue | T |
Categorias formais
decltype(ci) ≡ const int
decltype((ci)) ≡ const int&
decltype(((ci))) ≡ const int&
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
Refletindo sobre decltype
Uma maneira (não-oficial) de interpretar o rationale de decltype com expressões anônimas é combinando-o com as regras de avaliação de expressão da linguagem.
O valor de um objeto é o próprio objeto.
Ponteiro: necessário de-referenciação explícita.
Referência: de-referenciação acontece implicitamente.
47
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
Refletindo sobre decltype
Uma maneira (não-oficial) de interpretar o rationale de decltype com expressões anônimas é combinando-o com as regras de avaliação de expressão da linguagem.
O valor de um objeto é o próprio objeto.
Ponteiro: necessário de-referenciação explícita.
Referência: de-referenciação acontece implicitamente.
A avaliação de uma referência dispara, automaticamente, uma de-referenciação do objeto, o qual tem tipo base igual ao tipo da própria referência. Considerá-lo de tipo T& ou T&& é, sem perda de expressividade, mais abrangente (referências são desestruturadas) do que considerá-lo de tipo T.
48
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
Verificação de compreensão
Regras de decltype para uma expressão de tipo base int e categoria especificada.
49
?
?
?
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
Verificação de compreensão
Regras de decltype para uma expressão de tipo base int e categoria especificada.
50
int.
int&.
int&&.
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
Exercícios
51
© Leandro T. C. Melo - Curso C++
Algumas referências
52
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com
53
Curso de C++ moderno
Perguntas?
© Leandro T. C. Melo - www.ltcmelo.com
© Leandro T. C. Melo - www.ltcmelo.com