2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

可変長引数テンプレートでコンストラクタのラッパーをすっきり書く

Last updated at Posted at 2015-02-20

みなさん、元気に-std=c++11してますか?
C++初心者ですが、なかなかすっきり書けたので投稿してみます。

##概要

可変長引数テンプレートを使って、
任意のクラスの任意のコンストラクタのラッパーを少ないコードで実現できます。

##困った状況

まず、何の変哲もない、以下のコードがあります。

class A {
};

int main() {
    A* a = new A();
    delete a;
}

しかし、直接Aのnewを呼ばせたくない、という事情が出てきたとします。
例えば、

  • newの後に必ず何らかの処理を行いたい(メモリ管理とか)
  • newじゃなくて自分でallocateするかもしれなくなるとか
  • Facadeを介して、インターフェースをまとめたい

みたいな感じです。

そんなわけで、間に一つクラスを挟むことにします。
名前は適当に、Facadeにしておきましょう。

class A {
};

class Facade {
public:
    A* create_A() {
        A* a = new A();
        // なんやかんやの処理
        return a;
    }
};

int main() {
    Facade f;
    A* a = f.create_A();
    delete a;
}

はいできました。

さて、コードを書き進めて、Aのコンストラクタが増えたとしましょう。
こんな感じです。

class A {
public:
    A();
    A(int i);
    A(string s);
};

じゃあ、Facadeにもメソッド増やさないとだめですね。

class A {
public:
    A();
    A(int i);
    A(string s);
};

class Facade {
public:
    A* create_A() {
        A* a = new A();
        // なんやかんやの処理
        return a;
    }
    A* create_A(int i) {
        A* a = new A(i);
        // なんやかんやの処理
        return a;
    }
    A* create_A(string s) {
        A* a = new A(s);
        // なんやかんやの処理
        return a;
    }
};

int main() {
    Facade f;
    A* a1 = f.create_A();
    delete a1;
    A* a2 = f.create_A(1234);
    delete a2;
    A* a3 = f.create_A("wowow");
    delete a3;
}

できましたね。既に心の中ではイラッとし始めていますが。
しかも、今度は他のクラスもこのFacade経由でオブジェクトを作ることになったりしたら、

class A {
public:
    A();
    A(int i);
    A(string s);
};

class B {
public:
    B();
    B(int i, string s);
    B(int i, string s, double d);
};

class Facade {
public:
    A* create_A() {
        A* a = new A();
        // なんやかんやの処理
        return a;
    }
    A* create_A(int i) {
        A* a = new A(i);
        // なんやかんやの処理
        return a;
    }
    A* create_A(string s) {
        A* a = new A(s);
        // なんやかんやの処理
        return a;
    }

    B* create_B() {
        B* b = new B();
        // なんやかんやの処理
        return b;
    }

    B* create_B(int i, string s) {
        B* b = new B(i, s);
        // なんやかんやの処理
        return b;
    }

    B* create_B(int i, string s, double d) {
        B* b = new B(i, s, d);
        // なんやかんやの処理
        return b;
    }
};

int main() {
    Facade f;
    A* a1 = f.create_A();
    delete a1;
    A* a2 = f.create_A(1234);
    delete a2;
    A* a3 = f.create_A("wowow");
    delete a3;

    B* b1 = f.create_B();
    delete b1;
    B* b2 = f.create_B(5678, "foo");
    delete b2;
    B* b3 = f.create_B(0, "bar", 1.0);
    delete b3;
}

うわー、ちょっとなんとかならんのかいな、と思いますよね。私は思いました。
しかしtemplateを使うにしても、コンストラクタの引数の数も型もわからんのではどうしようもない……

##解決策

いや、待てよ?
可変長引数テンプレートってやつがあるらしいが、あいつでなんとかならんのかな?
と思って調べてみて、少しごにょごにょしたらできました。

class A {
public:
    A();
    A(int i);
    A(string s);
};

class B {
public:
    B();
    B(int i, string s);
    B(int i, string s, double d);
};

class Facade {
public:
    template <class T, class... Types>
    T* create(Types... values)
    {
        T* g = new T(values...);
        // なんやかんやの処理
        return g;
    }
};

int main() {
    Facade f;
    A* a1 = f.create<A>();
    delete a1;
    A* a2 = f.create<A>(1234);
    delete a2;
    A* a3 = f.create<A>("wowow");
    delete a3;

    B* b1 = f.create<B>();
    delete b1;
    B* b2 = f.create<B>(5678, "foo");
    delete b2;
    B* b3 = f.create<B>(0, "bar", 1.0);
    delete b3;
}

おお!メソッド一つでいけた!こいつぁいい!
クラスやコンストラクタが増えてもおかまいなしや!

##ちなみに

この方法は便利になった分、「特定のコンストラクタはFacadeから呼ばせたくない」
場合には困りますが、その場合は特殊化してエラー吐かせるとかで対処してくださいね☆

2
3
3

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?