Muokkaa

Jaa


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, or union 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.