Aliases e typedefs (C++)
Você pode usar uma declaração de alias para declarar um nome para uso como sinônimo de um tipo declarado anteriormente. (Esse mecanismo também é chamado informalmente de alias de tipo). Você também pode usar esse mecanismo para criar um modelo de alias, o que pode ser útil para alocadores personalizados.
Sintaxe
using identifier = type;
Comentários
identificador
O nome do alias.
tipo
O identificador de tipo para o qual você está criando um alias.
Um alias não introduz um novo tipo e não podem alterar o significado de um nome de tipo existente.
A forma mais simples de alias é equivalente ao mecanismo typedef
de C++03:
// C++11
using counter = long;
// C++03 equivalent:
// typedef long counter;
Essas duas formas permitem a criação de variáveis do tipo counter
. Algo mais útil seria um alias de tipo para std::ios_base::fmtflags
como este:
// C++11
using fmtfl = std::ios_base::fmtflags;
// C++03 equivalent:
// typedef std::ios_base::fmtflags fmtfl;
fmtfl fl_orig = std::cout.flags();
fmtfl fl_hex = (fl_orig & ~std::cout.basefield) | std::cout.showbase | std::cout.hex;
// ...
std::cout.flags(fl_hex);
Os aliases também funcionam com ponteiros de função, mas são muito mais legíveis que o typedef equivalente:
// C++11
using func = void(*)(int);
// C++03 equivalent:
// typedef void (*func)(int);
// func can be assigned to a function pointer value
void actual_function(int arg) { /* some code */ }
func fptr = &actual_function;
Uma restrição do mecanismo typedef
é que ele não funciona com modelos. No entanto, a sintaxe de alias de tipo no C++11 permite a criação de modelos de alias:
template<typename T> using ptr = T*;
// the name 'ptr<T>' is now an alias for pointer to T
ptr<int> ptr_int;
Exemplo
O exemplo a seguir demonstra como usar um modelo de alias com um alocador personalizado – nesse caso, um tipo de vetor de inteiro. Você pode substituir qualquer tipo por int
para criar um alias conveniente para ocultar as listas de parâmetros complexas em seu código funcional principal. Ao usar o alocador personalizado em todo o seu código, você pode melhorar a legibilidade e reduzir o risco de introduzir bugs causados por erros de digitação.
#include <stdlib.h>
#include <new>
template <typename T> struct MyAlloc {
typedef T value_type;
MyAlloc() { }
template <typename U> MyAlloc(const MyAlloc<U>&) { }
bool operator==(const MyAlloc&) const { return true; }
bool operator!=(const MyAlloc&) const { return false; }
T * allocate(const size_t n) const {
if (n == 0) {
return nullptr;
}
if (n > static_cast<size_t>(-1) / sizeof(T)) {
throw std::bad_array_new_length();
}
void * const pv = malloc(n * sizeof(T));
if (!pv) {
throw std::bad_alloc();
}
return static_cast<T *>(pv);
}
void deallocate(T * const p, size_t) const {
free(p);
}
};
#include <vector>
using MyIntVector = std::vector<int, MyAlloc<int>>;
#include <iostream>
int main ()
{
MyIntVector foov = { 1701, 1764, 1664 };
for (auto a: foov) std::cout << a << " ";
std::cout << "\n";
return 0;
}
1701 1764 1664
Typedefs
Uma declaração typedef
introduz um nome que, dentro de seu escopo, se torna um sinônimo para o tipo fornecido pela parte de declaração do tipo da declaração.
Você pode usar declarações de typedef para construir nomes mais curtos ou mais significativos para os tipos já definidos pelo idioma ou para os tipos que você declarou. Os nomes de typedef permitem que você encapsule os detalhes da implementação que podem ser alterados.
Em contraste com declarações class
, struct
, union
e enum
, as declarações typedef
não introduzem novos tipos — introduzem novos nomes para tipos existentes.
Os nomes declarados usando typedef
ocupam o mesmo espaço de nome de outros identificadores (exceto os rótulos de instrução). Portanto, não podem usar o mesmo identificador de um nome declarado anteriormente, exceto em uma declaração de tipo de classe. Considere o seguinte exemplo:
// typedef_names1.cpp
// C2377 expected
typedef unsigned long UL; // Declare a typedef name, UL.
int UL; // C2377: redefined.
As regras de ocultação de nome pertencentes a outros identificadores também controlam a visibilidade dos nomes declarados usando typedef
. Portanto, o exemplo a seguir é válido em C++:
// typedef_names2.cpp
typedef unsigned long UL; // Declare a typedef name, UL
int main()
{
unsigned int UL; // Redeclaration hides typedef name
}
// typedef UL back in scope
Outra instância de ocultação de nome:
// typedef_specifier1.cpp
typedef char FlagType;
int main()
{
}
void myproc( int )
{
int FlagType;
}
Ao declarar um identificador de escopo local com o mesmo nome de um typedef
, ou ao declarar um membro de uma estrutura ou de uma união no mesmo escopo ou em um escopo interno, o especificador do tipo deverá ser especificado. Por exemplo:
typedef char FlagType;
const FlagType x;
Para reutilizar o nome FlagType
para um identificador, um membro da estrutura ou da união, o tipo deve ser fornecido:
const int FlagType; // Type specifier required
Não é suficiente indicar
const FlagType; // Incomplete specification
pois FlagType
é tomado como parte do tipo, não como um identificador que redeclarado. Esta declaração é considerada uma declaração inválida, semelhante a:
int; // Illegal declaration
Você pode declarar qualquer tipo com typedef
, incluindo o ponteiro, função e tipos de matriz. Você pode declarar um nome de typedef para um ponteiro para um tipo de estrutura ou união antes de definir o tipo da estrutura ou de união, contanto que a definição tenha a mesma visibilidade da declaração.
Exemplos
Um uso de declarações typedef
é tornar as declarações mais uniformes e compactas. Por exemplo:
typedef char CHAR; // Character type.
typedef CHAR * PSTR; // Pointer to a string (char *).
PSTR strchr( PSTR source, CHAR target );
typedef unsigned long ulong;
ulong ul; // Equivalent to "unsigned long ul;"
Para usar typedef
para especificar tipos fundamentais e derivados na mesma declaração, você pode separar os declaradores com vírgulas. Por exemplo:
typedef char CHAR, *PSTR;
O exemplo a seguir fornece o tipo DRAWF
para uma função que não retorna valor e que usa dois argumentos int:
typedef void DRAWF( int, int );
Após a instrução typedef
anterior, a declaração
DRAWF box;
é equivalente à declaração
void box( int, int );
typedef
é combinado com frequência com struct
para declarar e nomear tipos definidos pelo usuário:
// typedef_specifier2.cpp
#include <stdio.h>
typedef struct mystructtag
{
int i;
double f;
} mystruct;
int main()
{
mystruct ms;
ms.i = 10;
ms.f = 0.99;
printf_s("%d %f\n", ms.i, ms.f);
}
10 0.990000
Redeclaração de typedefs
A declaração typedef
pode ser usada para redeclarar o mesmo nome para fazer referência ao mesmo tipo. Por exemplo:
Arquivo de origem file1.h
:
// file1.h
typedef char CHAR;
Arquivo de origem file2.h
:
// file2.h
typedef char CHAR;
Arquivo de origem prog.cpp
:
// prog.cpp
#include "file1.h"
#include "file2.h" // OK
O arquivo prog.cpp
inclui dois arquivos de cabeçalho, os quais contêm declarações typedef
para o nome CHAR
. Contanto que as duas declarações façam referência ao mesmo tipo, essa redeclaração é aceitável.
Um typedef
não pode redefinir um nome que foi declarado anteriormente como um tipo diferente. Considere essa alternativa file2.h
:
// file2.h
typedef int CHAR; // Error
O compilador emite um erro em prog.cpp
devido à tentativa de redeclarar o nome CHAR
para fazer referência a um tipo diferente. Essa política se estende a construtos como:
typedef char CHAR;
typedef CHAR CHAR; // OK: redeclared as same type
typedef union REGS // OK: name REGS redeclared
{ // by typedef name with the
struct wordregs x; // same meaning.
struct byteregs h;
} REGS;
typedefs em C++ vs. C
O uso do especificador typedef
com tipos de classe tem amplo suporte devido à prática de ANSI C de declaração de estruturas sem nome em declarações typedef
. Por exemplo, muitos desenvolvedores de C usam o seguinte idioma:
// typedef_with_class_types1.cpp
// compile with: /c
typedef struct { // Declare an unnamed structure and give it the
// typedef name POINT.
unsigned x;
unsigned y;
} POINT;
A vantagem dessa declaração é que ela permite declarações como:
POINT ptOrigin;
em vez de:
struct point_t ptOrigin;
No C++, a diferença entre os nomes de typedef
e os tipos reais (declarados com as palavras-chave class
, struct
, union
e enum
) é mais distinta. Embora a prática de C de declarar uma estrutura sem nome em uma instrução typedef
ainda funcione, ela não fornece nenhum benefício notacional como em C.
// typedef_with_class_types2.cpp
// compile with: /c /W1
typedef struct {
int POINT();
unsigned x;
unsigned y;
} POINT;
O exemplo anterior declara uma classe chamada POINT
usando a sintaxe typedef
da classe sem nome. POINT
é tratado como um nome de classe; no entanto, as seguintes restrições se aplicam a nomes introduzidos dessa forma:
O nome (o sinônimo) não pode aparecer após um prefixo
class
,struct
ouunion
.O nome não pode ser usado como nomes de construtor ou destruidor dentro de uma declaração da classe.
Em resumo, essa sintaxe não fornece um mecanismo para herança, construção ou destruição.