模板推导/类型转换错误

时间:2017-03-15 11:31:12

标签: c++ templates type-conversion

我在模板扣除/类型转换方面遇到问题。以下代码无法编译:

template <typename Type>
struct A
{
    void DoA()
    {
    }
};

struct B : public A<B>
{
    void DoB()
    {
    }
};

template <typename T>
void DoSmth(const std::shared_ptr<A<T>> &p)
{
    p->DoA();
}

int _tmain(int argc, _TCHAR* argv[])
{
    std::shared_ptr<B> pb(new B);
    DoSmth(pb);

    //std::shared_ptr<A<B>> pa(pb);
    //DoSmth(pa);
    return 0;
};

MSVC错误如下:

error C2664: 'void DoSmth<B>(const std::shared_ptr<A<B>> &)' : cannot convert argument 1 from 'std::shared_ptr<B>' to 'const std::shared_ptr<A<B>> &'
Binding to reference with added qualification
followed by
Call to constructor 'std::shared_ptr<A<B>>::shared_ptr<B,void>(const std::shared_ptr<B> &) throw()'
c:\program files (x86)\microsoft visual studio 12.0\vc\include\memory(531) : see declaration of 'std::shared_ptr<A<B>>::shared_ptr'
followed by
Binding to reference

海湾合作委员会错误:

prog.cpp:28:11: error: no matching function for call to ‘DoSmth(std::shared_ptr<B>&)’
  DoSmth(pb);
           ^
prog.cpp:21:6: note: candidate: template<class T> void DoSmth(const std::shared_ptr<A<T> >&)
 void DoSmth(const std::shared_ptr<A<T>> &p)
      ^~~~~~
prog.cpp:21:6: note:   template argument deduction/substitution failed:
prog.cpp:28:11: note:   mismatched types ‘A<T>’ and ‘B’
  DoSmth(pb);
           ^

注释代码(当手动完成转换时)不会发生问题,如果i_a不是模板类,也不会发生问题。我想知道为什么编译器不能进行转换,如果(根据错误信息)它已经推断出模板类型(这样它应该等同于注释代码)。

在不改变函数的参数类型(我迫切需要它为A<T>)的情况下,我可以做些什么吗?请注意,shared_ptr类仅用于说明问题,我实际上使用了另一个智能指针类,我可以轻松更改(如果需要在那里进行更改)。

请注意,如果使用原始指针而不是智能指针,一切都像魅力一样!

2 个答案:

答案 0 :(得分:5)

这里的问题是void DoSmth(const std::shared_ptr<A<T>> &p) T是一种依赖类型,无法推断出来。所以我们需要给编译器一些帮助。值得庆幸的是,我们可以使用SFINAEstd::is_base_of来约束模板。如果我们使用

template <typename T>
void DoSmth(const std::shared_ptr<T> &p)

然后T现在是可推导的,但是这个函数将接受任何不是我们想要的共享指针。我们只需要一个T A<T>或从中派生的东西。使用std::enable_ifstd::is_base_of,我们可以像

一样
template <typename T, typename = typename std::enable_if<std::is_base_of<A<T>, T>::value>::type>
void DoSmth(const std::shared_ptr<T> &p)
{
    p->DoA();
}

现在我们可以将std::shared_ptr<B>传递给函数而无需做任何额外的事情。下面是一个显示它正常工作的示例,它也拒绝了shared_ptr非派生类型:

#include <type_traits>
#include <memory>

template <typename Type>
struct A
{
    void DoA()
    {
    }
};

struct B : public A<B>
{
    void DoB()
    {
    }
};

struct Foo {};

template <typename T, typename = typename std::enable_if<std::is_base_of<A<T>, T>::value>::type>
void DoSmth(const std::shared_ptr<T> &p)
{
    p->DoA();
}

int main()
{
    std::shared_ptr<B> pb(new B);
    DoSmth(pb); // this compiles
    std::shared_ptr<Foo> pf(new Foo);
    DoSmth(pf); // this will generate a compiler error

    //std::shared_ptr<A<B>> pa(pb);
    //DoSmth(pa);
    return 0;
}

Live Example

答案 1 :(得分:2)

您可以明确指定类型。

int main()
{
    std::shared_ptr<B> pb(new B);
    DoSmth<B>(pb);
    //-----^
}