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);
}

Visual Studio 2012 を使用すると、「エラー C2668: 'IsBase' : オーバーロードされた関数へのあいまいな呼び出し」が表示されますが、ここでコードを試しても同じ結果が得られます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

1 に答える 1

4

これはテンプレートの制限ですか、これが機能しない別の理由はありますか、それともバグですか?

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

ただし、いくつかの 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; }
于 2013-03-28T21:00:58.747 に答える