CentOS7 で EPEL 由来の Clang (3.4.2) を使って std::enable_shared_from_this を使った以下のようなコードをコンパイルすると、何故かリンクエラーになります。(要 -std=c++11

#include <memory>

struct C;

struct A : public std::enable_shared_from_this<A> {
        struct B;
        friend struct C;
private:
        A() {}
public:
        static std::shared_ptr<A> createB();
        static std::shared_ptr<A> createC();
        static std::shared_ptr<A> createD();
};

// ネストクラス(クラス内クラス)を使う場合
struct A::B : public A {
        B() : A() {}
};

std::shared_ptr<A> A::createB()
{
        return std::make_shared<B>(); // NG
}

// フレンドクラスを使う場合
struct C : public A {
        C() : A() {}
};

std::shared_ptr<A> A::createC()
{
        return std::make_shared<C>(); // OK
}

// ローカルクラス(関数内クラス)を使う場合
std::shared_ptr<A> A::createD()
{
        struct D : public A {
                D(): A() {}
        };
        return std::make_shared<D>(); // OK
}

int main(void)
{
        auto b = A::createB();
        auto c = A::createC();
        auto d = A::createD();
}
/tmp/enable_make_shared-7126d3.o: In function `std::__shared_ptr<A::B, (__gnu_cxx::_Lock_policy)2>::__shared_ptr<std::allocator<A::B>>(std::_Sp_make_shared_tag, std::allocator<A::B> const&)':
enable_make_shared.cc:(.text._ZNSt12__shared_ptrIN1A1BELN9__gnu_cxx12_Lock_policyE2EEC2ISaIS1_EJEEESt19_Sp_make_shared_tagRKT_DpOT0_[_ZNSt12__shared_ptrIN1A1BELN9__gnu_cxx12_Lock_policyE2EEC2ISaIS1_EJEEESt19_Sp_make_shared_tagRKT_DpOT0_]+0x8b): undefined reference to `void std::__enable_shared_from_this_helper<A, A::B>(std::__shared_count<(__gnu_cxx::_Lock_policy)2> const&, std::enable_shared_from_this<A> const*, A::B const*)'

この構造は private なコンストラクタを持つクラスを(そのクラスの中で) std::make_shared を使って構築する際に使われるパターンなんですが、std::enable_shared_from_this と組み合わせるとクラス構造によっては何故かうまくいきません。

もっと新しい libstdc++ と Clang 3.5 との組み合わせでは OK らしいのですが、CentOS7 で Clang 3.5 をビルドしてそれを使ってコンパイルするとやっぱりリンクエラーが出るので、CentOS7 の libstdc++ と Clang との「相性が悪い」のではないかと推察しています。具体的に何が悪いのかまではスキル不足で追えていませんが。

ちなみに、上記のローカルクラスを使う場合ですが、CentOS6 の標準コンパイラである GCC 4.4.7 の -std=c++0x では使えません(Status of Experimental C++0x Support in GCC 4.4 で “Local and unnamed types as template arguments” が No なので)。CentOS6 + GCC 4.4.7 と CentOS7 Clang 3.4.2 の両方を対象にしようとするとフレンドクラスを使うか #ifdef で分けることになります。(ただし、フレンドクラスを使うとクラス外から構築できる穴ができてしまうのでオススメできません)

Trackback

no comment untill now

Add your comment now