読者です 読者をやめる 読者になる 読者になる

altebute.hatenablog.com

犬も歩けば規格にあたる

配列の代わりに std::tuple を使って不要なキャストを避ける

要素を指定したら自動で型を判別しダウンキャストする - 雑記

上記の記事では、基底クラス型のポインタの配列に対して、 任意の添え字に対して静的に型が決っている派生クラスのアドレスを保持し、 テンプレート関数を用いて派生クラスポインタ型のアドレスを取得している。

この記事を見てなんか違和感があったのでよく考えてみたのだが、 配列に入るオブジェクトの型をコンパイル時に決定しているのなら、 キャストをせずにアドレスを取得する事が可能な筈だ。

std::tupleを用いてアドレスを保持すれば良い。 上記の記事のコードを、単純にstd::tupleを用いたコードに変更すると、以下のようになる。

#include <iostream>
#include <tuple>

enum class TYPE_NUMBER
{
    A,
    B,
    C,
    TYPE_MAX
};

struct A
{
    void Print()
    {
        std::cout << "A" << std::endl;
    }
};

struct B : public A
{
    void Print()
    {
        std::cout << "B" << std::endl;
    }
};

struct C : public A
{
    void Print()
    {
        std::cout << "C" << std::endl;
    }
};

std::tuple<A*, B*, C*> ptrTuple;

int main(void)
{
    using namespace std;
    
    get<static_cast<size_t>(TYPE_NUMBER::A)>(ptrTuple) = new A();
    get<static_cast<size_t>(TYPE_NUMBER::B)>(ptrTuple) = new B();
    get<static_cast<size_t>(TYPE_NUMBER::C)>(ptrTuple) = new C();
    
    auto Aptr = get<static_cast<size_t>(TYPE_NUMBER::A)>(ptrTuple);
    auto Bptr = get<static_cast<size_t>(TYPE_NUMBER::B)>(ptrTuple);
    auto Cptr = get<static_cast<size_t>(TYPE_NUMBER::C)>(ptrTuple);
    
    cout << Aptr << endl;
    cout << Bptr << endl;
    cout << Cptr << endl;
    
    Aptr->Print();
    Bptr->Print();
    Cptr->Print();
    
    delete Aptr;
    delete Bptr;
    delete Cptr;
    
    return 0;
}

ptrTupleはローカル変数にしてもよい。 もっと言えば、型をautoにしておいて、std::make_tuple関数に動的確保したアドレスを渡せば、型の記述を省略できる。

int main(void)
{
    ...
    auto ptrTuple = make_tuple(new A(), new B(), new C());
    
    auto Aptr = get<static_cast<size_t>(TYPE_NUMBER::A)>(ptrTuple);
    ...
}

上記のコードであれば、一切のキャストが発生しない。

しかしながら、上記のコードは元のコードとは異なり、range-based forによるdeleteが実現できていない。 std::tupleが保持する要素に対するアクセスは、std::get関数で行われるが、 この関数はテンプレート関数であるため、コンパイル時定数を用いてアクセスする必要があるからだ。 このため、通常の配列と比較すると、要素へのアクセスがやや面倒な場面がある。 キャストや、仮想関数のコールが大きなボトルネックにならないのであれば、 このような場面でstd::tupleを用いて実装する必要性は薄いかもしれない。

次回の記事では、std::tupleに対してstd::for_each的な処理を行いたい。