エイリアスと typedef (C++)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 particularly useful for custom allocators.

構文Syntax

using identifier = type;

解説Remarks

identifieridentifier
エイリアスの名前。The name of the alias.

typetype
エイリアスを作成する型識別子。The type identifier you are creating an alias for.

エイリアスでは新しい型は定義されず、既存の型名の意味を変更することはできません。An alias does not introduce a new type and cannot change the meaning of an existing type name.

エイリアスの最も単純な形式は、C++ 03 のメカニズムに相当し typedef ます。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 enable the creation of variables of type "counter". 次のような std::ios_base::fmtflags の型エイリアスが便利な場合があります。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);

エイリアスは関数ポインターでも使用できますが、同等の typedef よりもはるかに読みやすくなります。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;

このメカニズムの制限は、 typedef テンプレートでは機能しないということです。A limitation of the typedef mechanism is that it doesn't work with templates. しかし、C++11 での型エイリアスの構文ではエイリアス テンプレートを作成できます。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. の任意の型をに置き換えて、 int 便利なエイリアスを作成し、メインの機能コード内の複雑なパラメーターリストを非表示にすることができます。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

TypedefsTypedefs

宣言では typedef 、そのスコープ内で、宣言の 型宣言 の部分で指定された型のシノニムになる名前が導入されます。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.

typedef 宣言を使用して、既に言語で定義されている型や、宣言した型に対して、より短い、またはわかりやすい名前を作成できます。You can use typedef declarations to construct shorter or more meaningful names for types already defined by the language or for types that you have declared. Typedef 名を使用して、変更可能な実装の詳細をカプセル化できます。Typedef names allow you to encapsulate implementation details that may change.

class、、 struct union 、およびの宣言とは異なり enumtypedef 宣言は新しい型を導入しません。既存の型に新しい名前が導入されます。In contrast to the class, struct, union, and enum declarations, typedef declarations do not introduce new types — they introduce new names for existing types.

を使用して宣言さ typedef れた名前は、他の識別子と同じ名前空間を使用します (ステートメントラベルを除く)。Names declared using typedef occupy the same namespace as other identifiers (except statement labels). したがって、クラス型の宣言以外では、以前に宣言された名前と同じ識別子を使用できません。Therefore, they cannot 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.

他の識別子に関連する名前を隠す規則は、を使用して宣言された名前の可視性も制御し typedef ます。The name-hiding rules that pertain to other identifiers also govern the visibility of names declared using typedef. したがって、次の例は C++ で有効です。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
// typedef_specifier1.cpp
typedef char FlagType;

int main()
{
}

void myproc( int )
{
    int FlagType;
}

Typedef と同じ名前でローカル スコープの識別子を宣言するとき、あるいは同じスコープまたは内部スコープで構造体または共用体のメンバーを宣言するときは、型指定子を指定する必要があります。When declaring a local-scope identifier by the same name as a typedef, or when declaring 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;

識別子、構造体メンバー、または共用体メンバーに対して FlagType 名を再利用するには、次のように型を指定する必要があります。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 is not sufficient to say

const FlagType;      // Incomplete specification

FlagType は再宣言された識別子ではなく、型の一部であると見なされるためです。because the FlagType is taken to be part of the type, not an identifier that is being redeclared. この宣言は、次のような正しくない宣言であると見なされます。This declaration is taken to be an illegal declaration like

int;  // Illegal declaration

ポインター、関数、配列型を含め、あらゆる型を typedef で宣言できます。You can declare any type with typedef, including pointer, function, and array types. 構造体型または共用体型を定義する前に、構造体型または共用体型へのポインターの typedef 名を宣言できます。ただし、定義が宣言と同じ可視性である必要があります。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

宣言の用途の1つ typedef は、宣言をより均一でコンパクトにすることです。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;"

を使用して typedef 同じ宣言で基本型と派生型を指定するには、宣言子をコンマで区切ります。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;

次の例は、値を返さず、2 つの int 引数を受け取る関数に対する型 DRAWF を用意します。The following example provides the type DRAWF for a function returning no value and taking two int arguments:

typedef void DRAWF( int, int );

上記のステートメントの後 typedef 、宣言After the above typedef statement, the declaration

DRAWF box;

は次の宣言と同等です。would be equivalent to the declaration

void box( int, int );

typedef は、 struct ユーザー定義型を宣言して名前を指定するために、と組み合わせて使用されることがよくあります。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

typedef の再宣言Re-declaration of typedefs

宣言は、同じ typedef 型を参照するために同じ名前を再宣言するために使用できます。The typedef declaration can be used to redeclare the same name to refer to the same type. 次に例を示します。For example:

// FILE1.H
typedef char CHAR;

// FILE2.H
typedef char CHAR;

// PROG.CPP
#include "file1.h"
#include "file2.h"   // OK

プログラム PROG。CPP には、2つのヘッダーファイルが含まれています。どちらにも名前の宣言が含まれてい typedef CHAR ます。The program 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.

は、 typedef 以前に別の型として宣言された名前を再定義することはできません。A typedef cannot redefine a name that was previously declared as a different type. したがって、FILE2 の場合は です。H containsTherefore, if FILE2.H contains

// FILE2.H
typedef int CHAR;     // Error

コンパイラは、名前 CHAR を再宣言して異なる型を参照しようとするため、エラーが発生します。the compiler issues an error because of the attempt to redeclare the name CHAR to refer to a different type. このことは、次のような構造もに及びます。This 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;

C++ と C の typedeftypedefs in C++ vs. C

typedef クラス型での指定子の使用は、宣言で名前のない構造体を宣言する ANSI C の手法によって主にサポートされてい typedef ます。Use of the typedef specifier with class types is supported largely because of the ANSI C practice of declaring unnamed structures in typedef declarations. たとえば、多くの C プログラマは次のように記述します。For example, many C programmers use the following:

// 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;

C++ では、 typedef 名前と実際の型 (、、、およびの各キーワードで宣言) の違い class struct union enum がより明確になります。In C++, the difference between typedef names and real types (declared with the class, struct, union, and enum keywords) is more distinct. ステートメントで無名の構造体を宣言する C の手法は typedef 引き続き機能しますが、c の場合と同様に数値表記の利点はありません。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;

前の例では、名前のない POINT クラス構文を使用して、という名前のクラスを宣言して typedef います。The preceding example declares a class named POINT using the unnamed class typedef syntax. POINT はクラス名として扱われますが、この方法で導入された名前には次の制限が適用されます。POINT is treated as a class name; however, the following restrictions apply to names introduced this way:

  • 名前 (シノニム) は class 、、 struct 、またはプレフィックスの後には記述できません unionThe name (the synonym) cannot appear after a class, struct, or union prefix.

  • 名前は、クラス宣言内のコンストラクター名またはデストラクター名として使用できません。The name cannot be used as constructor or destructor names within a class declaration.

つまり、この構文には、継承、構築、または破棄のための機能はありません。In summary, this syntax does not provide any mechanism for inheritance, construction, or destruction.