gl5_progのメモ

自分のためのメモとかまとめとか

C++で前方宣言したクラスの内部クラスへのポインタを引数にとる関数を作成するアイデア

はじめに

C++では前方宣言したクラスの内部クラスをさらに前方宣言することはできません。

class A;
class A::Inner; // error
class B
{
public:
    void Func( A::Inner* p ); // error
};

なので、内部クラスへのポインタを引数にとる関数を宣言するには、外部クラス(class A)の定義が必要になってしまいます。このせいでC++の内部クラスは使いにくくなっています。

#include "A.h" // しぶしぶインクルード
class B
{
public:
    void Func( A::Inner* p ); // ok
};

アイデア

テンプレート関数とその特殊化でごまかす。 http://ideone.com/fbf1bI

#include <stdio.h>
// B.h =================
class A;
/* 内部クラスは前方宣言できない
class A::Inner;
*/
class B
{
public:
    void Func( A* p );
    /* 前方宣言してない(できない)のでエラー
   void Func( A::Inner* p ){}
   */
    template<class T>
    void Func( T* p );
};
// B.cpp =================
class A
{
public:
    class Inner
    {};
};
void B::Func( A* p ){ printf( "Func( A* p )\n" ); }
template<> void B::Func<A::Inner>( A::Inner* p ){ printf( "Func<A::Inner>( A::Inner* p )\n" ); }
int main( void )
{
    B b;
    A a;
    A::Inner inner;
    b.Func( &a );
    b.Func( &inner );
    return 0;
}

テンプレート関数の特殊化をcppファイル側に書くことで、B.hでA.hをインクルードせずにA::Inner*を引数にとるメンバ関数の定義に成功しました!

ただし、この方法だとB::Func( A::Inner* )を呼び出せるのはB.cpp内からのみになります。が、使える場面はあるでしょう。

この方法は、内部クラスのポインタを引数にしたい場合だけではなく、前方宣言が面倒とか、オーバーロード関数の宣言が面倒とか言うときにも使えそうですね。行儀悪いですが。

それにしても内部クラスを前方宣言できないのは納得がいかないというか…。名前空間は似たようなことができるのに。

namespace A
{
    class Inner;
}
class B
{
public:
    void Func( A::Inner* p );
};