C++ での vs typedef の使用

Jay Shaw 2023年10月12日
  1. C++ の using キーワード
  2. C++ の typedef キーワード
  3. テンプレートの定義における typedefusing の使用
  4. まとめ
C++ での vs typedef の使用

この記事では、typedefusing を区別しようとします。 C++ の関数型プログラミングでは、これらのキーワードの目的とセマンティクスは同じであるため、両者の違いはごくわずかです。

この記事では、読者がこれらのキーワードの機能と typedefusing の違いを確実に理解できるように、さまざまなコンテキストでキーワードについて説明します。

C++ の using キーワード

typedef とは対照的に using キーワードを理解することは、typedefusing の違いを学ぶために重要です。

オブジェクトをプログラムの現在のスコープに移動するために使用されます。 これは、C++ から指定子を取り込むためのアクセス ツールとして使用できることを意味します。

例を見てみましょう。

#include <iostream>

int main() {
  using std::cout;
  using std::string;

  int a = 56;
  cout << a;
  return 0;
}

出力:

56

ここでは、キーワード using を使用して、名前空間から coutstring などの指定子にアクセスし、それらをプログラムのスコープ内に取り込みます。

同様に、using を使用して他の指定子にアクセスしたり、using namespace as std を使用して名前空間全体を取得したりすることもできます。 これにより、名前空間全体がプログラムのスコープ内に移動し、すべての指定子に std を使用してアクセスできます。

using キーワードの例

以下のコード スニペットは、パラメーターとして 2つの整数変数 ab を持つパブリック メソッド add を持つクラス Parent を作成します。 メソッド add は、2つの変数の合計を出力します。

child クラスは保護モードで Parent クラスを派生させます。つまり、Parent クラスのすべてのメンバーは保護されたメンバーとして継承されます。

main 関数内では、Child クラスを使用してオブジェクトのインスタンスが作成され、メソッド add はそのオブジェクトを使用して派生します。

#include <iostream>
using namespace std;
class Parent {
 public:
  void add(int a, int b) { cout << "Result = " << a + b << endl; }
};
class Child : protected Parent {
 public:
  using Parent::add;
};
int main() {
  Child obj;
  obj.add(15, 30);
  return 0;
}

出力:

Result = 45

ここで、キーワード using は、Child クラスを介して Parent クラスからメソッド add にアクセスするために使用されます。

C++ で using キーワードを使用してループを実装する

ここでの構文は、using を介して for ループを実装しています。

for (using i = int; i{} != 0;) {
  i++;
}

範囲ベースのループの場合、using は次のように使用されます。

std::vector<int> v{1, 2, 3};
for (using Foo = int; Foo f : v) {
  (void)f;
}

C++ でのusingキーワードを使用したSwitch Caseステートメント

switch case ステートメントでは、using は次のように実装されます。

if (using Foo = int; true) {
  (void)Foo{};
}
switch (using Foo = int; 0) {
  case 0:
    (void)Foo{};
}

C++ の typedef キーワード

キーワード typedef には、カスタム名を使用して型に名前を付ける機能があります。 これは、既存のデータ型にエイリアスを導入できることを意味します。

typedefusing の主な違いは、このセクションで確認できます。

以下のプログラムは、この概念を明確に説明しています。

#include <stdio.h>
#include <string.h>

typedef struct Books {
  char title[50];
  char author[50];
  char subject[100];
  int book_id;
} Book;

4つの変数を持つ struct クラス Book が導入されています。 これら 4つの変数は、typedef を使用して新しいデータ型として構造化され、この型には Book という名前が付けられます。

main クラス内で、新しいオブジェクトを作成して型 Book にアクセスし、その変数を取得する必要があります (必要に応じて)。

個々の変数が構文 object_name.variable_name,"input" を使用して呼び出されていることがわかります。 これらの変数にデータが与えられると、呼び出しに使用されたのと同じメソッドを使用して出力されます。

int main() {
  Book book;

  strcpy(book.title, "Typedef vs using");
  strcpy(book.author, "JS");
  strcpy(book.subject, "C Programming");
  book.book_id = 6495407;

  printf("Book title : %s\n", book.title);
  printf("Book author : %s\n", book.author);
  printf("Book subject : %s\n", book.subject);
  printf("Book book_id : %d\n", book.book_id);

  return 0;
}

出力:

Book title : Typedef vs using
Book author : JS
Book subject : C Programming
Book book_id : 6495407

C++ で typedef キーワードを使用したループ

ループの反復中に、typedef は構文 for (typedef (datatype)Function; Function{} != 0;) を使用して定義されます。

for (typedef int i; i{} != 0;) {
  i++;
}

範囲ベースのループ反復中、typedef は次のように使用されます。

std::vector<int> v{1, 2, 3};
for (typedef int Foo; Foo f : v)
//   ^^^^^^^^^^^^^^^ init-statement
{
  (void)f;
}

2 次元行列内の範囲ベースのループの別の例:

for (typedef struct {
       int x;
       int y;
     } P;
     auto [x, y] : {P{1, 1}, {1, 2}, {3, 5}}) {
  (void)x;
  (void)y;
}

C++ でtypedefキーワードを使用したSwitch Caseステートメント

以下に示すように、typedef キーワードは switch case ステートメントで使用されます。

if (typedef int Foo; true) {
  (void)Foo{};
}

switch (typedef int Foo; 0) {
  case 0:
    (void)Foo{};
}

テンプレートの定義における typedefusing の使用

関数型プログラミングのコンテキストから、typedefusing の違いを確認しました。 理解する必要があるもう 1つの重要な部分は、テンプレートの定義方法です。

C++ では、typedefs にテンプレートのエイリアスを定義する機能がありました。 非常に長い間使用されてきましたが、typedef のセマンティクスがテンプレートとうまくいかないことが観察されました。

この問題を解決するために、typedef は減価償却され、using が行われました。

以下の例では、typedefusing の違いについて、テンプレートの定義のコンテキストで説明しています。

C++ での typedef を使用したエイリアス テンプレート

例 1:

ここでは、長さ x とテンプレートを使用してエイリアスを与える必要がある静的な幅を持つように長方形が定義されています。

<size_t N> は、長方形の長さを格納するテンプレートです。 対照的に、Dimensiontypedef を使用してエイリアス Rectangle が与えられるオブジェクトです。

ここで、コードは RectangleDimension<N,1> のエイリアスに割り当てます。これは、オブジェクト Dimension が長さと幅を持つことを意味します。 ここで、オブジェクト Dimension は、長さ <size_t N> と幅 1 を持ちます。

template <size_t N>
struct Rectangle {
  typedef Dimension<N, 1> type;
};

これは、プログラマが単一の値パラメータ size_t NRectangle に渡す必要があることを意味します。 コンパイラは渡されたものを長さとして受け取り、幅は 1 に設定されます。

構文 Rectangle<5>::int は整数値 5 をオブジェクト Dimension に渡し、Dimension<5,1> を書くのと同じになります。

上記の例は、typedef が廃止されたため、最近の C++ バージョンでは使用されなくなりました。 using を使用してテンプレートにエイリアスを与えることは、上記の例よりも簡単です。

例 2:

テンプレート クラス Match は、以下のコード スニペットで定義されています。 この Match クラスには、エイリアス テンプレート MatchBox が与えられます。

Match クラスも将来的に置き換えることができます。

template <typename U>
struct Match {};

template <typename U>
struct MatchBox {
  typedef Match<U> type;
};

MatchBox<int>::type variable;

template <typename V>
struct bar {
  typename MatchBox<V>::type_var_member;
}

型の抽象化が必要であるが、将来提供できるようにテンプレート パラメータも保持する必要がある場合、このようなコードを記述する必要があります。

C++ での using キーワードによるテンプレートのエイリアス

例 1:

このセクションでは、テンプレートのコンテキストにおける typedefusing の違いについて説明します。 ここで、テンプレート <size_t N>typedef の例 1 で使用したものと同じで、長方形の長さを格納します。

struct を使用する代わりに、エイリアス Rectangle がキーワード using によってオブジェクト Dimension に割り当てられます。 これは、オペレーターが割り当てられるのと同じ方法で行われます。

エイリアスを割り当てた後のエイリアス Rectangle は、縦と横にそれぞれ N1 を持ちます。

template <size_t N>
using Rectangle = Dimension<N, 1>;

using はより洗練されたバージョンのエイリアシングを提供し、記述と実装がより簡単であることがわかります。 ここで使用されている構文は、従来の変数と演算子の割り当てに似ているため、読みやすさが改善されたようです。

例 2:

この例では、テンプレート パラメーターを開いたままにして、指定できるようにします。 typedef の例では、このプロセスに複数行のコードが必要でした。 しかし、using 構文はコードと複雑さを軽減します。

template <typename U>
using MatchBox = Match < U

                             MatchBox<int>
                                 variable;
template <typename V>
struct bar {
  MatchBox<V> _var_member;
}

using キーワードを使用してテンプレートのエイリアスを定義するのは、typedef よりもはるかに簡単です。これは、プロセスが変数のコピー割り当てに似ているためです。

まとめ

この記事では、C++ におけるキーワード typedefusing の違いについて説明します。 読者は、これらのキーワードの基本的な違いと、さまざまなプログラミング コンテキストでの実装を理解できます。

この記事が学習の旅に役立つことを願っています。