2011-01-25 13 views
2

私の状況は以下の通りです:テンプレート関数のオーバーロードは

私は値の状況を処理し、手動でポインタを扱うかさえnewすることなく、NULL可能なされているオブジェクトのテンプレートラッパを持っています。これは、基本的にはこれに沸く:

私は実際に値を取得したり、その関数にそれを渡すようにしようとする前にそれがnullに設定されているかどうかを確認するために、 null_tダミーに対してチェックできる比較演算子と今
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; 
}; 

その値を必要とし、自動変換を行います。

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

だから私は、それぞれが適切なデータとXML-DOMを埋めるこれら

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

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

のような機能を持っています。これも正しく動作します。

今すぐ、しかし、これらの構造体の一部がコードに

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内でのみ使用されます。言い換えれば、この問題が発生した外部プログラムであっても、テンプレートオーバーロードされた関数で頭が含まれていません。それはテンプレートバージョンを見つけたが、フィットし、他の機能、例えば、あなたのタイプの基本クラスを使用して1以上のテンプレート「完全一致」を選択する前

+0

名前空間を定義していますか?あなたの定義はどの名前空間にありますか?そして、図書館のこと? – T33C

+1

堅牢で完璧なソリューションのためには、 'boost :: optional'を見てみてください。 – GManNickG

+0

@GMan:はい、それは 'nullable'と非常によく似ていますが、' boost :: optiona'は同じ自動変換動作をしているので、手元で問題を解決できません。 – Mephane

答えて

0

これまで本当の答えは見つからなかったので、私は回避策を講じました。基本的に、私は別々のdetail名前空間に前述のAdd機能を入れ、2つのテンプレートラッパー関数を追加しました:

template<class T> 
    xml_iterator Add(xml_iterator parent, const char* name, 
        const T& value) 
    { 
     return detail::Add(parent, name, value); 
    } 

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

私は、これは常に適切にこれら2つの関数の正しいものに解決することが判明し、実際の機能あなたが見ることができるように、これらの内部の別々のステップで選択されます。

0

コンパイラは、主に完全一致]を選択します。

暗黙の変換は危険であり、しばしばあなたを噛みます。それは単純に、あなたがあなたのヘッダーやあなたが使っている名前空間を含むようにすることができます。

は、私は、次の操作を行います。

  • は、明示的なNULL可能すべてのあなたのコンストラクタを作成します。厳密に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を命名機能を有する&変換を交換します。非constの場合はref()を、constの場合はcref()を使用します。

私はまた、(3のルールのために必要)

  • 代入演算子
  • operator-> 2つのオーバーロードあなたはconst性を伝播されているように、クラスを完了します。

これをC++ 0xに使用する場合は、r値のコピーと割り当てもできます(この場合は便利です)。

ところで、ディープコピーはスライスされるベースクラスでは機能しません。

+0

ええ、私は代入、 ' - >'演算子などを削除しました。クラスは具体的な型を保持するためのもので、 'int x = 42;という変数を持つ代わりに' nullable y = 42; '。テンプレートは、基本クラスへのポインタのみを格納することによって多型を扱うことを目的としていません。 – Mephane

関連する問題