模板推导/类型转换错误

Art*_*kov 2 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;
};
Run Code Online (Sandbox Code Playgroud)

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
Run Code Online (Sandbox Code Playgroud)

GCC错误:

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);
           ^
Run Code Online (Sandbox Code Playgroud)

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

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

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

Nat*_*ica 5

这里的问题是in 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)
Run Code Online (Sandbox Code Playgroud)

然后T现在可以推导,但是这个函数将接受任何不是我们想要的共享指针.我们只希望T这是一个A<T>什么的是从它衍生出来的.使用std::enable_if,std::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();
}
Run Code Online (Sandbox Code Playgroud)

现在我们可以传递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;
}
Run Code Online (Sandbox Code Playgroud)

Live Example