2

私の状況は次のとおりです。

ポインターやnew. これは基本的に次のようになります。

struct null_t
{
  // just a dummy
};
static const null_t null;

template<class T> class nullable
{
public:
  nullable()
    : _t(new T())
  {}

  nullable(const nullable<T>& source)
    : _t(source == null ? 0 : new T(*source._t))
  {}

  nullable(const null_t& null)
    : _t(0)
  {}

  nullable(const T& t)
    : _t(new T(t))
  {}

  ~nullable()
  {
    delete _t;
  }

  /* comparison and assignment operators */

  const T& operator*() const
  {
    assert(_t != 0);
    return *_t;
  }

  operator T&()
  {
    assert(_t != 0);
    return *_t;
  }

  operator const T&() const
  {
    assert(_t != 0);
    return *_t;
  }
private:
  T* _t;
};

比較演算子を使用すると、null_t実際に値を取得する前にダミーが null に設定されているかどうかを確認したり、その値を必要として自動変換を行う関数に渡したりすることができます。

このクラスは、問題に遭遇するまで、かなり長い間役に立ちました。すべてファイル (この場合は XML) に出力されるいくつかの構造体を含むデータ クラスがあります。

だから私はこれらのような機能を持っています

xml_iterator Add(xml_iterator parent, const char* name,
                 const MyDataStruct1& value);

xml_iterator Add(xml_iterator parent, const char* name,
                 const MyDataStruct2& value);

それぞれが適切なデータで XML-DOM を埋めます。これも正しく動作します。

ただし、現在、これらの構造体の一部はオプションであり、コードでは として宣言されます。

nullable<MyDataStruct3> SomeOptionalData;

このケースを処理するために、テンプレートのオーバーロードを作成しました。

template<class T>
xml_iterator Add(xml_iterator parent, const char* name,
                 const nullable<T>& value)
{
  if (value != null)  return Add(parent, name, *value);
  else                return parent;
}

私の単体テストでは、期待どおり、値または構造がnullable<T>.

ただし、前述のデータ クラス (独自の DLL でエクスポートされる) を使用すると、何らかの理由で最後のテンプレート関数を初めて呼び出す必要があり、代わりに からnullable<T>それぞれの型への自動変換Tが行われ、関数を完全にバイパスします。このケースを処理します。上で述べたように、すべての単体テストは 100% うまくいき、テストとコードを呼び出す実行可能ファイルの両方がデバッグ モードで MSVC 2005 によってビルドされています。

更新:明確にするために-オーバーロードされたAdd関数はエクスポートされず、DLL 内で内部的にのみ使用されます。つまり、この問題が発生する外部プログラムには、テンプレートのオーバーロードされた関数を含むヘッドさえ含まれていません。

4

2 に答える 2

0

コンパイラは、テンプレート化されたバージョンを見つける前に主に完全一致を選択しますが、適合する別の関数 (たとえば、型の基本クラスを使用する関数) よりもテンプレート化された "完全一致" を選択します。

暗黙の変換は危険であり、しばしば噛み付きます。単に、ヘッダーまたは使用している名前空間をインクルードしている可能性があります。

私は次のことをします:

  • Nullable のコンストラクターをすべて明示的にします。これは、パラメーターを 1 つだけ取るか、1 つのパラメーターで呼び出すことができる任意のコンストラクターで行います (既定値を持つコンストラクターが他にもある場合でも)。

    template<class T> class nullable
    
    {
      public:
        nullable() 
           : _t(new T())
        {}
    
    
    explicit nullable(const nullable<T>& source)
       : _t(source == null ? 0 : new T(*source._t))
    {}
    
    explicit nullable(const null_t& null)
        : _t(0)
      {}
    
      explicit nullable(const T& t)
        : _t(new T(t))
      {}
    // rest
    };
    
  • 演算子 T& conversions を名前付き関数に置き換えます。非 const には ref() を使用し、const には cref() を使用します。

また、クラスを完了するには

  • 代入演算子 (3 のルールに必要)
  • constness を伝播しているときの operator-> 2 つのオーバーロード。

これを C++0x にも使用する予定がある場合は、右辺値のコピーと代入もこの場合に役立ちます。

ところで、基本クラスはスライスされるため、ディープ コピーは基本クラスでは機能しないことを知っています。

于 2011-01-25T09:47:20.143 に答える