Help us understand the problem. What is going on with this article?

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

More than 3 years have passed since last update.

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

動機

内部で 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パターンって知ってる?」
「……あー、うん……」

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした