【问题标题】:Issue with std::shared_ptr, inheritance, and template argument deductionstd::shared_ptr、继承和模板参数推导问题
【发布时间】:2023-03-11 07:59:01
【问题描述】:

我正在尝试将模板参数推导与继承和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>

【问题讨论】:

  • 这是 GCC 4.7.3 对此的评价,仅供参考:t.cpp:9:6: note: template argument deduction/substitution failed: t.cpp:16:47: note: mismatched types 'Base&lt;T&gt;' and 'Derived' t.cpp:16:47: note: 'std::shared_ptr&lt;Derived&gt;' is not derived from 'std::shared_ptr&lt;Base&lt;T&gt; &gt;'

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


【解决方案1】:

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

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

在不考虑用户定义的转换的情况下,编译器无法推断出T 会使shared_ptr&lt;Base&lt;T&gt;&gt;shared_ptr&lt;Derived&gt; 相同或shared_ptr&lt;Derived&gt; 的基类(再一次,shared_ptr&lt;Base&lt;int&gt;&gt;不是shared_ptr&lt;Derived&gt; 的基类。

因此,类型推导失败。

要解决这个问题,你可以让你的函数的参数是一个简单的shared_ptr&lt;T&gt; 并且添加一个 SFINAE-constraint 来确保你的重载只有在参数派生自(或者是)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

【讨论】:

  • 不幸的是,decltype 等人。在 MSVC10 中不可用。为什么不使用is_base_of
  • @DyP:decltype 等。 在 MSVC10 中可用
  • @DyP:嗯,似乎只有decltype 可用,而不是“al.:” :D 好吧,在这种情况下,我需要稍微改变一下,你是对的
  • @AndyProwl 感谢您的详尽回答。这与我在看到错误消息之前一直在思考的思路一致。令我困惑的是,它并没有说它不能专门化模板,而是说它不能将std::shared_ptr&lt;Derived&gt; 转换为std::shared_ptr&lt;Base&lt;int&gt;&gt;。这看起来好像确实发生了模板参数推导。你怎么看?
  • @Ari:老实说,我认为编译器只是想帮您一个忙,并根据您的预期解释事情:您希望发生派生到基的转换因为Derived 派生自Base&lt;int&gt;,但这不会发生,因为shared_ptr&lt;Derived&gt; 不能转换为shared_ptr&lt;Base&lt;int&gt;&gt;
【解决方案2】:

这样写就可以了:

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2014-08-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2021-08-10
    相关资源
    最近更新 更多