std::shared_ptr、继承和模板实参演绎的问题



我试图使用模板参数演绎继承和std::shared_ptr。在下面的示例代码中可以看到,我将一个shared_ptr<Derived>传递给一个模板化的非成员函数,该函数应该执行模板参数推导。如果我手动命名类型一切正常,如果我让它做模板参数推导它就不会。看起来好像编译器无法找出类型,然而错误消息显示它可以。我不知道这是怎么回事,如果有任何建议我都很感激。(Visual Studio 2010)

#include <memory>
template <typename T>
class Base {};
class Derived : public Base<int> {};
template <typename T>
void func(std::shared_ptr<Base<T> > ptr) {};
int main(int argc, char* argv[])
{
   std::shared_ptr<Base<int>> myfoo(std::shared_ptr<Derived>(new Derived)); // Compiles
   func(myfoo);    // Compiles
   func<int>(std::shared_ptr<Derived>(new Derived));  // Compiles
   func(std::shared_ptr<Derived>(new Derived));  // Doesn't compile. The error message suggests it did deduce the template argument.
   return 0;
}

错误信息:

5> error C2664: 'func' : cannot convert parameter 1 from 'std::tr1::shared_ptr<_Ty>' to 'std::tr1::shared_ptr<_Ty>'
5>          with
5>          [
5>              _Ty=Derived
5>          ]
5>          and
5>          [
5>              _Ty=Base<int>
5>          ]
5>          Binding to reference
5>          followed by
5>          Call to constructor 'std::tr1::shared_ptr<_Ty>::shared_ptr<Derived>(std::tr1::shared_ptr<Derived> &&,void **)'
5>          with
5>          [
5>              _Ty=Base<int>
5>          ]
5>          c:Program Files (x86)Microsoft Visual Studio 10.0VCincludememory(1532) : see declaration of 'std::tr1::shared_ptr<_Ty>::shared_ptr'
5>          with
5>          [
5>              _Ty=Base<int>
5>          ]
5>

虽然编译器可以在进行类型推导时执行派生到基的转换,但std::shared_ptr<Derived> 并不是本身派生自std::shared_ptr<Base<int>>

两者之间有一个用户定义的转换,允许shared_ptr在多态性方面表现得像普通指针,但是编译器在执行类型演绎时不会考虑用户定义的转换。

如果不考虑用户定义的转换,编译器不能推导出使shared_ptr<Base<T>>shared_ptr<Derived>shared_ptr<Derived>相同的T(同样,shared_ptr<Base<int>>而不是shared_ptr<Derived>的基类)。

因此,类型演绎失败。

要解决这个问题,可以让函数的形参是一个简单的shared_ptr<T> ,并且添加一个sfinae约束,以确保只有当参数的类型派生自(或是)Base类模板的实例时才选择重载:

#include <type_traits>
namespace detail
{
    template<typename T>
    void deducer(Base<T>);
    bool deducer(...);
}
template <typename T, typename std::enable_if<
    std::is_same<
        decltype(detail::deducer(std::declval<T>())),
        void
        >::value>::type* = nullptr>
void func(std::shared_ptr<T> ptr)
{
    // ...
}

下面是一个的实例

如果你这样写就可以了:

template <typename T>
void func(std::shared_ptr<T> ptr) {};

如果你真的想显式地阻止函数被非Base派生的东西调用,你可以使用type_traits/enable_if/etc

相关内容

  • 没有找到相关文章

最新更新