Slip Ahead Logging

It's not your fault at all.

CRTP & typedef による型の追加と継承の相性

boost::add_pointer のようなことをやりたいと思い,

#include <iostream>
#include <tr1/memory>

template <typename T> class Pointable {
public:
    typedef std::tr1::shared_ptr<T> ptr_t;
};

class Parent : public Pointable<Parent> {
public:
    Parent() {
        std::cout << "Parent created" << std::endl;
    }

    ~Parent() {
        std::cout << "Parent deleted" << std::endl;
    }
};

class Child : public Pointable<Child>, public Parent {
public:
    Child() {
        std::cout << "Child created" << std::endl;
    }

    ~Child() {
        std::cout << "Child deleted" << std::endl;
    }
};

int main(int argc, char **argv)
{
    Child::ptr_t child_ptr(new Child());
    Parent::ptr_t parent_ptr(new Parent());

    return 0;
}

をコンパイルすると,

pointable.cc:39:12: error: member 'ptr_t' found in multiple base classes of different types
    Child::ptr_t child_ptr(new Child());
           ^
pointable.cc:8:37: note: member found by ambiguous name lookup
    typedef std::tr1::shared_ptr<T> ptr_t;
                                    ^
pointable.cc:8:37: note: member found by ambiguous name lookup
1 error generated.

というエラーが出てしまう(clang++).

次のように,各クラス内で明示的にポインタ型を typedef してあげれば,当然ながらコンパイルできる.しかし,元々の目的はこの冗長さを取り除くことにあったので,できれば上記のように CRTP などを用いたい.

class Parent {
public:
    typedef std::tr1::shared_ptr<Parent> ptr_t;

    Parent() {
        std::cout << "Parent created" << std::endl;
    }

    ~Parent() {
        std::cout << "Parent deleted" << std::endl;
    }
};

class Child : public Parent {
public:
    typedef std::tr1::shared_ptr<Child> ptr_t;

    Child() {
        std::cout << "Child created" << std::endl;
    }

    ~Child() {
        std::cout << "Child deleted" << std::endl;
    }
};

何かよい解決策はないものだろうか.

追記

C++ 人脈に乏しいため Stackoverflow 様にお頼り申し上げた.返信は来るかな?

http://stackoverflow.com/questions/9114959/typedef-with-crtp-doesnt-work-when-inheritance-is-used

追記2

わー,返信が頂けたよ.

Your problem has nothing to do with the CRTP, but with multiple inheritance. Child inherits ptr_t from both its base classes, and both types are different: shared_ptr vs. shared_ptr. Therefore, the compiler cannot figure out which type you mean by Child::ptr_t in main.

As you pointed out, you have to fix this manually using a typedef in Child (making your Pointable base class useless, though).

class Child : public Parent,
              public Pointable<Child> {
public:
    typedef Pointable<Child>::ptr_t ptr_t;
http://stackoverflow.com/questions/9114959/typedef-with-crtp-doesnt-work-when-inheritance-is-used

ああ,そうか,多重継承をしているためであったのね.理解した.

追記3

ちなみに,上記の回答で

class Child : public Parent,
              public Pointable<Child> {
public:
    typedef Pointable<Child>::ptr_t ptr_t;

というコードを教えて頂いているが,こちらも少し冗長ではあるが実際の定義である shared_ptr を取り除くことができており DRY の原則には反していないので OK だろう.勉強になったなあ.

こちらについては,次のページも参照のこと.いやあ Stack Overflow さまさま.

http://stackoverflow.com/questions/8497984/how-to-inherit-all-typedefs-from-one-base-class-under-multiple-inheritance