もの凄くバッドノウハウ臭がしますが、思い付いてしまったのでつい。
動機
内部で private
な型を定義しているクラスがあるとします。
このクラスのヘルパーを定義したいけれども、このヘルパー内でも上記クラスの private
な型を使いたいといった場合を考えます。
この場合、ヘルパーが private
な型にアクセスできるようにするためには、いくつか考えられますが、どうにも一長一短です。
クラス内の型を
private
にするのを諦め、public
にする。
無関係な所にまで公開したくないんだけどな……クラス内にヘルパーを定義する。
公開する必要のない要素でヘッダが膨れ上がってしまう。ヘルパーを
friend
にする。
ヘルパーを匿名名前空間に定義したい場合、この手が使えない。namespace detail
など、明示的に名前を付けると、他の単位との衝突が怖いし、ヘルパーか名前空間名の先頭に自クラス名を付けるなどして回避させると名前が長い。
そこで、ヘルパーをテンプレートとして宣言し、使いたい型を、元のクラスの方からテンプレート引数として渡す方法を思い付きました。
実装例
ソース
#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_
#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パターンって知ってる?」
「……あー、うん……」