具有共享指针参数歧义的函数重载

时间:2013-03-28 20:41:16

标签: c++ inheritance c++11 shared-ptr overloading

我想创建重载函数,这些函数采用指向基类和派生类的共享指针。它似乎适用于引用和原始指针,但不适用于额外派生类的共享指针。请参阅示例代码:

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

1 个答案:

答案 0 :(得分:4)

  

这是模板的限制,还有另一个原因导致这不起作用或者它是一个错误吗?

不,这不是一个错误。事实上,你似乎已经遇到了智能指针的唯一陷阱:std::shared_ptr<base>可以从std::shared_ptr<derived>以及std::shared_ptr<extra_derived>构建,但这两个转换序列都不比other(是两个用户定义的相同长度的转换序列)。

但是,您仍然可以通过使用一些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; }