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

C4251警告をまじめに対応してはまった

昨日の続き。C4251警告について。

class Context {
public:
  static std::auto_ptr<Context> GetInstance(){
    return s_singleton;
  }
  Context();
  ~Context();
private:
  struct Impl;
  std::auto_ptr<Impl> pimpl_;
  static std::auto_ptr<Context> s_singleton;
};

もともとライブラリのヘッダがこんなだった場合。次のように書くとstd::auto_ptrがC4251警告になります。

#if DLLとしてビルドする場合
# define DLLIMPORT __declspec(dllexport)
#else
# define DLLIMPORT __declspec(dllimport)
#endif
class DLLIMPORT Context {//ここにimportを明記しないとs_singletonがリンクエラーになる
public:
  static const std::auto_ptr<Context>& GetInstance(){
    return s_singleton;
  }
  Context();
  ~Context();
private:
  struct Impl;
  std::auto_ptr<Impl> pimpl_;//C4251警告!
  static std::auto_ptr<Context> s_singleton;//C4251警告!
};

で、まじめに対応しようとすると、

  struct Impl;
  friend class DLLIMPORT std::auto_ptr<Context>;//export!!
  friend class DLLIMPORT std::auto_ptr<Impl>;//export!!
  std::auto_ptr<Impl> pimpl_;
  static std::auto_ptr<Context> s_singleton;

とするとdllビルド時にstd::auto_ptrがexportされ警告が消えます。しかし!!
実際リンクしてみるとdll側にはstd::auto_ptrのdll内で実際に使われたメソッドしかexportされてない(というかtemplateが実体化していない?)のにこのクラス全体をimportすることになり、例えばdllでauto_ptr::getを使わずにリンク先のexeでContext::GetInstance().get()とか呼び出すと「未解決の外部シンボル」エラーになります。
ちなみにこんなんもかけますが、もっとひどいことになります。

template<typename T> class DLLIMPORT std::auto_ptr;//いっそテンプレートごとexport
class DLLIMPORT {
  ... 以下略 ...

カンの良い人は想像できると思いますが、auto_ptrの実体すべてをdllからimportしようとします。もちろんそんなの無理です!
あー、なんかすげぇアホらしかった。疲れた。