LoginSignup
2
2

More than 5 years have passed since last update.

private な型をクラス外部のヘルパーで使う

Last updated at Posted at 2016-02-12

もの凄くバッドノウハウ臭がしますが、思い付いてしまったのでつい。

動機

内部で private な型を定義しているクラスがあるとします。
このクラスのヘルパーを定義したいけれども、このヘルパー内でも上記クラスの private な型を使いたいといった場合を考えます。

この場合、ヘルパーが private な型にアクセスできるようにするためには、いくつか考えられますが、どうにも一長一短です。

  1. クラス内の型を private にするのを諦め、 public にする。
    無関係な所にまで公開したくないんだけどな……

  2. クラス内にヘルパーを定義する。
    公開する必要のない要素でヘッダが膨れ上がってしまう。

  3. ヘルパーを friend にする。
    ヘルパーを匿名名前空間に定義したい場合、この手が使えない。 namespace detail など、明示的に名前を付けると、他の単位との衝突が怖いし、ヘルパーか名前空間名の先頭に自クラス名を付けるなどして回避させると名前が長い。

そこで、ヘルパーをテンプレートとして宣言し、使いたい型を、元のクラスの方からテンプレート引数として渡す方法を思い付きました。

実装例

ソース

hoge.hpp
#ifndef hoge_HPP_
#define hoge_HPP_

class hoge
{
private:
    enum struct internal_enum // private な型その1
    {
        AAA = 100,
        BBB = 200,
    };

    struct internal_type // private な型その2
    {
        internal_enum x;
    };

    internal_type v;

public:
    void f();
};

#endif // hoge_HPP_
hoge.cpp
#include "hoge.hpp"
#include <iostream>


// ヘルパーを匿名名前空間で定義したい。
namespace
{
#if 0
    // これだと、 internal_type, internal_enum が private だと怒られる。
    struct helper
    {
        const char *message;

        explicit helper(const hoge::internal_type &v)
        {
            switch(v.x)
            {
            case hoge::internal_enum::AAA:
                message = "AAA";
                break;

            case hoge::internal_enum::BBB:
                message = "BBB";
                break;

            default:
                message = "BUG!";
                break;
            }
        }
    };
#else
    // 怒られる型をテンプレートパラメータにして間接的に利用
    template<typename T, typename U>
    struct helper
    {
        const char *message;

        explicit helper(const T &v)
        {
            switch(v.x)
            {
            case U::AAA:
                message = "AAA";
                break;

            case U::BBB:
                message = "BBB";
                break;

            default:
                message = "BUG!";
                break;
            }
        }
    };
#endif
}


void hoge::f()
{
    v.x = internal_enum::AAA;
    // helper に、クラスから型を渡してあげる。
    helper<internal_type, internal_enum> h(v);
    std::cout << h.message << std::endl;
}


int main() // 試してみる
{
    hoge h;
    h.f();
    return 0;
}

出力例

$ g++ -std=c++11 hoge.cpp
$ ./a.out
AAA

実際使ってる?

使いたい場面があるのですが、冒頭にも書いた通り、あまりにもバッドノウハウ臭がひどいので使っていません (- -;
というか、利用する時に型名を渡すので、その分長くなってしまうわけで、それなら 3. ヘルパーを friend にする。 に書いた、元のクラス名を付けたヘルパーを定義すればいいじないか……

「ところでpimplパターンって知ってる?」
「……あー、うん……」

2
2
0

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
2