2013-03-28 12 views
6

基本クラスと派生クラスへの共有ポインタをとるオーバーロードされた関数を作成します。これは参照と生ポインタのために働くようですが、余分な派生クラスの場合は共有ポインタのためには機能しません。私が手共有ポインタの引数のあいまいさによる関数のオーバーロード

#include <memory> 

class Base{}; 
class Derived : public Base {}; 
class ExtraDerived : public Derived {}; 


bool IsBase(Base*){ return true; } 
bool IsBase(Derived*){ return false; } 

bool IsBase(std::shared_ptr<Base>){ return true; } 
bool IsBase(std::shared_ptr<Derived>){ return false; } 

int main() 
{ 
    auto derived = std::make_shared<Derived>(); 
    auto extra_derived = std::make_shared<ExtraDerived>(); 
    // works 
    auto raw_result_derived = IsBase(derived.get()); 
    auto raw_result_extra_derived = IsBase(extra_derived.get()); 
    auto shared_result_derived = IsBase(derived); 
    // doesn't work 
    auto shared_result_extra_derived = IsBase(extra_derived); 
} 

:「エラーC2668: 『IsBase』:オーバーロードされた関数のあいまいな呼び出しを」コード例を参照してくださいのVisual Studio 2012を使用しているとき、私はここにhttp://ideone.com/6uoa0p上でコードをしようとすると、私も同じ結果を得ます。

これは望ましい動作のようには見えません( '生のもの'の場合)。これはテンプレートの制限ですか、これがうまくいかない理由があるのでしょうか、バグですか? そして、どうやって少なくとも醜い方法で動作させることができますか?

私が思い付くことができる最高は

//ugly workaround 
bool IsBase(std::shared_ptr<Base>, Base*){ return true; } 
bool IsBase(std::shared_ptr<Derived>, Derived*){ return false; } 
template<typename T> bool IsBase(std::shared_ptr<T> input) 
{ 
    return IsBase(input, input.get()); 
} 
+4

あなたがそれを望むのはなぜ?基本関数のみを提供するだけで、派生クラスポインタをうまく受け入れることができます。派生クラスオブジェクトを捕捉したい場合は、関数の引数の動的型をディスパッチする必要があるため、メソッドはうまく動作しません。プログラマが渡す前に 'Ptr 'を 'Ptr 'に暗黙的に変換すると、このオーバーロードの手法は壊れてしまいます。 –

+0

@ JohannesSchaub-litb私はそのような状況は考えていませんでした。私は自分が望むものを考え直さなければならないかもしれない。 – Barabas

答えて

4

は、テンプレートの制限はこれがある、これは動作しないか、それはバグでは別の理由があるのでしょうか?

いいえ、バグではありません。あなたはスマートポインタの唯一の落とし穴に遭ったようです:std::shared_ptr<derived>std::shared_ptr<extra_derived>からstd::shared_ptr<base>を構築できますが、これらの2つの変換シーケンスのどれも他のものより優れていません(2つのユーザ定義変換シーケンス長さ)。

しかし、あなたはまだいくつかのSFINAE制約を使用して、過負荷を修正することができます:

#include <type_traits> 

// Selected for `std::shared_ptr<Base>` 
template<typename T, typename std::enable_if< 
    std::is_same<T, Base>::value>::type* = nullptr> 
bool IsBase(std::shared_ptr<T>){ return true; } 

// Selected for `std::shared_ptr<T>` where T is a class derived from Base, 
// but not Base itself 
template<typename T, typename std::enable_if< 
    std::is_base_of<Base, T>::value && 
    !std::is_same<T, Base>::value 
    >::type* = nullptr> 
bool IsBase(std::shared_ptr<T>){ return false; } 
+0

悲しいことに、これはVS2012ではうまくいきませんが、将来的にはうまくいくはずです。この単純なケースでは機能しますが、異なる派生クラスのオーバーロードが増えると問題になる可能性があります。私は実際にこのような機能を持たせたいのか、私の質問でJohannesSchaub-litbのコメントを考慮しないのかについて考える必要があります。 – Barabas

関連する問題