std :: shared_ptr,继承和模板参数推导的问题

时间:2013-06-03 23:13:41

标签: c++ templates inheritance c++11 smart-pointers

我正在尝试使用继承和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.0\VC\include\memory(1532) : see declaration of 'std::tr1::shared_ptr<_Ty>::shared_ptr'
5>          with
5>          [
5>              _Ty=Base<int>
5>          ]
5>

2 个答案:

答案 0 :(得分:10)

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

两者之间存在用户定义的转换,允许shared_ptr的行为类似于多态性的常规指针,但编译器在执行类型推导时不会考虑用户定义的转换。

在不考虑用户定义的转换的情况下,编译器无法推导出T使shared_ptr<Base<T>>shared_ptr<Derived>相同或基类shared_ptr<Derived>({1}}再次,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)
{
    // ...
}

这是live example

答案 1 :(得分:0)

如果你用这种方式写它是有效的:

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

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