C++11 範囲に基づく for 文

多くのスクリプト言語では for in の形式でコンテナーの要素にアクセスできます。 Boost や Qt などでは C++ でもなんとかそれを実現させようとマクロを駆使して for_each として定義されていました。
しかし、とうとう C++11 で範囲 for は言語機能として正式に追加されました。
今回はそんな 範囲 for について紹介します。

範囲 for とは

範囲 for というのは C++11 で The range-based for statement(範囲に基づく for 文) として規定されたもので、 配列、リストといったコンテナーの各要素に順にアクセスする for です。


プログラミング中では、配列などのコンテナーに対してループを回して要素にアクセスする というのはよく行う処理です。
その際、 今まではループカウンターを回したり、イテレーターを使っていたと思います。 範囲 for を使えば次のように書けます。
  vector<int> ary = { 3,2,9,6 };

  for (auto elem : ary) {
    cout << elem << endl;
  }  
ループカウンターやイテレーターを書く必要がなく、簡単に書けるようになります。 すっきりしたので、 "コンテナーの要素にアクセスしている" というコードの意図が分かり易くなるという利点もあります。
また、添え字アクセスと違い、イテレーターのようにランダムアクセス以外にも使える汎用性があります。

なお、この考えを一歩進めると関数型のデータ処理となり、 for の逐次処理以外のいろんな処理ができるようになります。

範囲 for の対象

範囲 for に使えるコンテナーは次の 3 つです。
  • STL のコンテナーなど begin(), end() メソッドを持つクラス
  • 配列
  • 初期化リスト
前章のコンテナー以外もサンプルをあげておきます。

配列:
  int cary[] = { 3,2,9,6 };
  
  for (auto elem : cary) {
    cout << elem << endl;
  }
初期化リスト:
  for (auto elem : { 3,2,9,6 }) {
    cout << elem << endl;
  }

要素の型

範囲 for の要素(elem)の定義にはコンテナーの要素の型を記述します。 これには C++11 に型変換として追加された auto が使えます。

その際、関数の引数のようにコンテナーに変更を加えるかどうかによって、 参照(auto&)か const 参照(const auto &)になります。

変更を加える:
  vector<string> strs = { "foo", "bar", "baz" };
  
  for (auto &elem : strs) {
    elem += "!";
  }
  // { "foo!", "bar!", "baz!" }   
変更を加えない:
  vector<string> strs = { "foo", "bar", "baz" };
  
  for (const auto &elem : strs) {
    cout << elem << endl;
  }  
ただし、最初のサンプルのように要素が int などサイズが小さい場合は、 コピーしても問題がないため、ただの auto にします。
また、参照ではなく、ポインターを指定することも出来ます。

std::map の場合

Boost の for_each の時は std::map だとちょっと面倒でした。 範囲 for の場合は問題なく使えます。
  map<string, int> strmap = {
      { "foo", 3 },
      { "bar", 2 },
      { "baz", 9 }
  };
  for (const auto &elem : strmap) {
    cout << elem.first << " - " << elem.second << endl;
  }
std::map の場合の要素は std::pair です。
範囲 for の対象は begin(), end() を持つ必要がありました。 すなわち、各要素にはイテレーターを使ってアクセスしています。
前章では要素の型をいくつか紹介しましたが、 「イテレーターから導けるものが型として使える」と言えます。

範囲 for は次の記述を簡略化したものと考えると分かりやすいと思います。
  for (auto itr = ary.begin() ; itr != ary.end() ; itr++) {
    auto elem = *itr;
    // 要素に対する処理
  }  

サンプルコード

記事中で紹介したサンプルコードのファイルです。


スポンサーサイト



 

C++ での Mixin の活用 : Comparable を使って比較演算子を簡単実装

オブジェクト指向プログラミングには Mixin という手法があります。 これを使えば、自作のクラスを比較可能な(Comparable)クラスにすることが簡単に出来ます。 今回は Comparable Mixin を例に C++ での Mixin のやり方について説明します。

Mixin とインターフェース

Mixin の前提として、 多重継承の問題点を把握しておく必要があります。 その辺の話や Mixin の概要に関しては以下の記事をみていただくとして、 C++ に限定して簡単に説明していきます。 多重継承の問題を避けるための代表的な方法はインターフェースです。
class IComparable
{
 public:
  virtual int Compare(const IComparable &other) const = 0;
};
インターフェースの制限は次の 2 つです。 この制限によりインタフェースクラスは多重継承しても問題が発生しないので、クラスに対して自由に継承させられます。
  • 属性(メンバー変数)を持たない
  • 操作(メンバー関数)は宣言のみで実装を持たない(純粋仮想関数)
インターフェースを使うことにはいろんなメリットがあります。
  • 利用側 : 操作(Compare) を持つことが保証される
  • 継承側 : 操作(Compare) の実装が強制される
例えば、ソートのような処理のインプットで ICompareble を指定すれば、 Compare() が使えることが保証されますし、 実際に ICompareble を継承するクラスでは Compare() を実装しないとコンパイルエラーとなります。

cpp_mixin_interface.png


インターフェースはインターフェースで有用です。 ただ、 多重継承で問題になるのは属性を持つことの方で、 操作の実装を禁止する必要はありません。
「操作の実装を持つ」ということは言い換えるとなんらかの「機能を持つ」ということです。 そういったクラスを継承すると元のクラスに機能を付加することになります。 継承によって機能を追加するためのクラスやその手法を Mixinと呼びます。

例えば、『鯨』というクラスを考えてみます。
ほ乳類、魚類から継承したとするとダイヤモンド継承(ひし形継承)と呼ばれる共通の祖先を持つクラスの継承となり、多重継承で最も問題になる形になります。

ダイヤモンド継承

Mixin を用いると「泳げる」(Swimable) という機能を共に持つとみなすことができます。 ダイヤモンド継承は避けられますし、こちらの方がより自然にクラスによるモデル化ができています。

Mixin

Comparable Mixin

Mixin をさらに Comparable(比較できる) Mixin を具体例として説明していきます。


次のような点クラスについて考えてみます。
ちなみに struct はデフォルトが public なことを除いて class と同じものです。
struct Point
{
  int x;
  int y;
};
点クラスのように 等比、比較演算子のオーバーロードを行うと使いやすくなるクラスは多いと思いますが、 毎回それらを実装するのは面倒です。
しかし、 それらの演算子は比較関数から導くことが出来ます。 これを実装したクラスが Comparable Mixin のクラスです。
template <class T>
class ComparableMixIn
{
 public:
  /// 比較用関数
  ///
  /// | 状態               |  戻り値  |
  /// |--------------------|----------|
  /// | other の方が大きい | 負の値   |
  /// | other と一致       |    0     |
  /// | other の方が小さい | 正の値   |
  /// 
  virtual int Compare(const T &other) const = 0;


  // 演算子の実装
  bool operator==(const T &other) const { return Compare(other) == 0; };
  bool operator!=(const T &other) const { return Compare(other) != 0; };
  bool operator< (const T &other) const { return Compare(other) <  0; };
  bool operator<=(const T &other) const { return Compare(other) <= 0; };
  bool operator> (const T &other) const { return Compare(other) >  0; };
  bool operator>=(const T &other) const { return Compare(other) >= 0; };
};
Mixin クラスの特徴は次のようなものです。 インターフェースと同様に属性を持たないので多重継承ができますが、 機能(操作の実装)を持っているところが違います。
  • 属性を持たない
  • キーとなる関数の宣言(純粋仮想関数)
  • キー関数を使って機能となる関数を実装
ここではキーとなる関数は Compare() です。 この Compare() を使って、 比較できる(Comparable)という機能、 すなわち 等比や比較演算子のオーバーロードの実装を行っています。


一方、使う側である点クラスでは ComparableMixIn を継承し、 Compare() を実装します。
ここには実装のちょっとしたテクニックがあります。 ComparableMixIn は継承先の型をとるテンプレートです。こうすることによって継承先の型による演算子のオーバーロードを可能にしています。
struct Point : public ComparableMixIn<Point>
{
   :

  /// 比較関数.
  /// x で比較し、同じなら y で比較
  virtual int Compare(const Point &other) const override
  {
    return ((x != other.x) ? (x - other.x) : (y - other.y));
  }
};
cpp_mixin.png

この Mixin によって、 点クラスは比較の機能をもつことができるようになります。
  Point a(1, 2), b(2, 3);

  cout << boolalpha;
  cout << "a == b : " << (a == b) << endl;
  cout << "a != b : " << (a != b) << endl;
  cout << "a <  b : " << (a <  b) << endl;
  cout << "a <= b : " << (a <= b) << endl;
  cout << "a >  b : " << (a >  b) << endl;
  cout << "a >= b : " << (a >= b) << endl;
実行結果 :
a == b : false
a != b : true
a <  b : true
a <= b : true
a >  b : false
a >= b : false
なお、Boost::Operators を使うと == から != 、 < から他の不等号を定義できます。 厳密に言うと < さえ定義されていれば、 すべての等、不等号 を導くことができます。 (a == b は a < b && b < a)
これは < 演算子をキー関数とした Mixin ということもできます。

ソースファイル

以上のように Comparable Mixin を使うと Compare() を実装するだけで、等号、不等号の機能を簡単に追加することができます。
大したコード量ではありませんが、ヘッダーを公開していますので、ダウンロード(リンク先を保存)して自由に使ってください。[MIT ライセンス]



 
このページをシェア
アクセスカウンター
アクセスランキング
[ジャンルランキング]
コンピュータ
26位
アクセスランキングを見る>>

[サブジャンルランキング]
プログラミング
8位
アクセスランキングを見る>>
カレンダー(アーカイブ)
プルダウン 降順 昇順 年別

02月 | 2017年03月 | 04月
- - - 1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31 -


はてな新着記事
はてな人気記事
ブロとも申請フォーム
プロフィール

yohshiy

Author:yohshiy
職業プログラマー。
仕事は主に C++ ですが、軽い言語マニアなので、色々使っています。

はてブ:yohshiy のブックマーク
Twitter:@yohshiy

サイト紹介
プログラミング好きのブログです。プログラミング関連の話題や公開ソフトの開発記などを雑多に書いてます。ただ、たまに英語やネット系の話になることも。