Aliases and typedefs (C++)
You can use an alias declaration to declare a name to use as a synonym for a previously declared type. (This mechanism is also referred to informally as a type alias). You can also use this mechanism to create an alias template, which can be useful for custom allocators.
Syntax
using identifier = type;
Remarks
identifier
The name of the alias.
type
The type identifier you're creating an alias for.
An alias doesn't introduce a new type and can't change the meaning of an existing type name.
The simplest form of an alias is equivalent to the typedef
mechanism from C++03:
// C++11
using counter = long;
// C++03 equivalent:
// typedef long counter;
Both of these forms enable the creation of variables of type counter
. Something more useful would be a type alias like this one for std::ios_base::fmtflags
:
// 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);
Aliases also work with function pointers, but are much more readable than the equivalent typedef:
// 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;
A limitation of the typedef
mechanism is that it doesn't work with templates. However, the type alias syntax in C++11 enables the creation of alias templates:
template<typename T> using ptr = T*;
// the name 'ptr<T>' is now an alias for pointer to T
ptr<int> ptr_int;
Example
The following example demonstrates how to use an alias template with a custom allocator—in this case, an integer vector type. You can substitute any type for int
to create a convenient alias to hide the complex parameter lists in your main functional code. By using the custom allocator throughout your code, you can improve readability and reduce the risk of introducing bugs caused by typos.
#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
A typedef
declaration introduces a name that, within its scope, becomes a synonym for the type given by the type-declaration portion of the declaration.
You can use typedef declarations to construct shorter or more meaningful names for types already defined by the language or for types that you've declared. Typedef names allow you to encapsulate implementation details that may change.
In contrast to the class
, struct
, union
, and enum
declarations, typedef
declarations don't introduce new types; they introduce new names for existing types.
Names declared using typedef
occupy the same namespace as other identifiers (except statement labels). Therefore, they can't use the same identifier as a previously declared name, except in a class-type declaration. Consider the following example:
// typedef_names1.cpp
// C2377 expected
typedef unsigned long UL; // Declare a typedef name, UL.
int UL; // C2377: redefined.
The name-hiding rules that pertain to other identifiers also govern the visibility of names declared using typedef
. Therefore, the following example is legal in 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
Another instance of name hiding:
// typedef_specifier1.cpp
typedef char FlagType;
int main()
{
}
void myproc( int )
{
int FlagType;
}
When you declare a local-scope identifier by the same name as a typedef
, or when you declare a member of a structure or union in the same scope or in an inner scope, the type specifier must be specified. For example:
typedef char FlagType;
const FlagType x;
To reuse the FlagType
name for an identifier, a structure member, or a union member, the type must be provided:
const int FlagType; // Type specifier required
It isn't sufficient to say
const FlagType; // Incomplete specification
because the FlagType
is taken to be part of the type, not an identifier that's being redeclared. This declaration is taken to be an illegal declaration, similar to:
int; // Illegal declaration
You can declare any type with typedef
, including pointer, function, and array types. You can declare a typedef name for a pointer to a structure or union type before you define the structure or union type, as long as the definition has the same visibility as the declaration.
Examples
One use of typedef
declarations is to make declarations more uniform and compact. For example:
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;"
To use typedef
to specify fundamental and derived types in the same declaration, you can separate declarators with commas. For example:
typedef char CHAR, *PSTR;
The following example provides the type DRAWF
for a function returning no value and taking two int arguments:
typedef void DRAWF( int, int );
After the above typedef
statement, the declaration
DRAWF box;
would be equivalent to the declaration
void box( int, int );
typedef
is often combined with struct
to declare and name user-defined types:
// 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
Redeclaration of typedefs
The typedef
declaration can be used to redeclare the same name to refer to the same type. For example:
Source file file1.h
:
// file1.h
typedef char CHAR;
Source file file2.h
:
// file2.h
typedef char CHAR;
Source file prog.cpp
:
// prog.cpp
#include "file1.h"
#include "file2.h" // OK
The file prog.cpp
includes two header files, both of which contain typedef
declarations for the name CHAR
. As long as both declarations refer to the same type, such redeclaration is acceptable.
A typedef
can't redefine a name that was previously declared as a different type. Consider this alternative file2.h
:
// file2.h
typedef int CHAR; // Error
The compiler issues an error in prog.cpp
because of the attempt to redeclare the name CHAR
to refer to a different type. This policy extends to constructs such as:
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 in C++ vs. C
Use of the typedef
specifier with class types is supported largely because of the ANSI C practice of declaring unnamed structures in typedef
declarations. For example, many C programmers use the following idiom:
// 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;
The advantage of such a declaration is that it enables declarations like:
POINT ptOrigin;
instead of:
struct point_t ptOrigin;
In C++, the difference between typedef
names and real types (declared with the class
, struct
, union
, and enum
keywords) is more distinct. Although the C practice of declaring a nameless structure in a typedef
statement still works, it provides no notational benefits as it does in C.
// typedef_with_class_types2.cpp
// compile with: /c /W1
typedef struct {
int POINT();
unsigned x;
unsigned y;
} POINT;
The preceding example declares a class named POINT
using the unnamed class typedef
syntax. POINT
is treated as a class name; however, the following restrictions apply to names introduced this way:
The name (the synonym) can't appear after a
class
,struct
, orunion
prefix.The name can't be used as a constructor or destructor name within a class declaration.
In summary, this syntax doesn't provide any mechanism for inheritance, construction, or destruction.