【C++】無名構造体でタプルを実現する


実はC++はtupleクラスを使わずにタプルの実現が出来てしまいます。 しかもラベル付きタプルですので、用途によってはtupleクラスよりも使い勝手は良いです。

無名構造体で代用

auto getDate() {
  struct { int year, month, day; } date = {2016, 12, 24};
  return date;
}

戻り値をauto型にしている所がミソです。値受け取りの際もautoで受け取る必要があります。

auto date = getDate();
printf("%d年", date.year);     // 2016年
printf("%d月", date.month);    // 12月
printf("%d日", getDate().day); // 24日

メンバ変数側のデストラクタもきちんと呼ばれますので、メンバ変数としてクラスや構造体を利用することも可能です。RVO等の最適化もしっかり効きます。

引数として使う場合

取得したタプルを他の関数の引数として渡す際にはテンプレートを使用する必要があります。

template<class T> void printDate(const T& date) {
  printf("%d-%d-%d\n", date.year, date.month, date.day);
}

printDate(getDate()); // 2016-12-24

利用シーン

またこの手のコードを書かなくても良くなるのは大きな利点だと思います。

bool has_error;
auto it = open(&has_error);
if (!has_error) {
  doit(it);
}

こう書けるようになります。

auto it = open();
if (!it.has_error) {
  doit(it);
}

open(&has_error);のような古臭いテクニックは、その場限りの小さなプログラムや使い捨てのプログラムを書く際によく使ってしまうのですが、今回の無名構造体タプルがあればこの手のコードがよりスッキリと書けるようになるので、相当便利なものになると思います。

C++は汎用化する必要のないような簡単なロジックを書く場合でも、やたらとクラスや列挙型を多用した壮大なコードを書かなければいけなかったり、冗長的で使いづらいライブラリの利用を強いられたりすることが多いので、この手の機能は大変重宝します。

応用

一応、列挙体の使用やメンバ関数の使用も可能です。

auto getFile() {
  enum class State { err = 0x2 };
  struct {
    State state;
    bool hasError() {
      return state == State::err;
    }
  } file = {State::err};
  return file;
}

auto x = getFile();
if (x.hasError()) puts("yes");
if (x.state == decltype(x.state)::err) puts("yes");

広告

関連するオススメの記事