如何在 C++ 中使用模板类型的子类传递模板函数参数?

Posted

技术标签:

【中文标题】如何在 C++ 中使用模板类型的子类传递模板函数参数?【英文标题】:How to pass a templated function parameter with a subclass of the templated type in C++? 【发布时间】:2020-05-31 22:14:37 【问题描述】:

编译以下人为的例子:

class Base
;

class Derived : public Base
;

template< typename T >
class A
;

class B

public:

    static void f( const A< Base >& ) 
;

int main()

    A< Base > tb;
    A< Derived > td;

    B::f( tb );
    B::f( td );

    return 0;

使用 g++-8 会出现以下错误:

error: no matching function for call to 'B::f(A<Derived>&)'
 B::f( td );
note:   no known conversion for argument 1 from 'A<Derived>' to 'const A<Base>&'

为什么?

既然Derived is-a Base 并且它不会覆盖任何Base 的东西,为什么我不能用Derived 代替a Base 在模板化函数参数中?

【问题讨论】:

A&lt; Base &gt;A&lt; Derived &gt; 是不同的、不相关的类。他们之间没有转换。它们之间根本不必有任何相似之处——它们可以有完全不同的成员。 【参考方案1】:

Derived 确实是从 Base 派生的,但这并不意味着 A&lt;Derived&gt; 因此必须从 A&lt;Base&gt; 派生。 C++ 模板不能以这种方式工作。

A&lt;Derived&gt; 是一个类,由A 模板实例化。你可以简单地声明:

class A_Derived 

     // ...

;

使用相同的成员(如果有的话),并且几乎得到相同的结果。 A&lt;Base&gt; 也一样。图片中没有其他内容,这两个班级绝对没有任何关系。你可以在这里画一个心理图,好像你做了以下声明:

class A_Derived 

;

class A_Base 

;

这几乎就是历史。您是否看到 A_Derived 明确派生自 A_Base 这里?显然不是。如果某些东西需要一个引用或指向A_Base 的指针,你不能给它A_Derived,因为这两个类彼此完全没有关系。它们是独立的类。

附:如果您愿意,可以将 A&lt;Derived&gt; 的显式特化声明为派生自 A&lt;Base&gt;,但特化是完全不同的主题...

【讨论】:

【参考方案2】:

模板实例,如A&lt;Base&gt;A&lt;Derived&gt;,是不同的类型。特别是即使BaseDerived 有继承关系,它们也没有任何继承关系。

有很多方法可以让你想要的东西发挥作用。

首先,您可以使 A&lt;Derived&gt; 显式派生自 A&lt;Base&gt;,但这意味着添加整个类定义。

template<>
class A<Derived> : public A<Base>
;

其次,您可以通过构造函数模板的形式提供从A&lt;Derived&gt;A&lt;Base&gt; 的隐式转换。您可以使用std::enable_ifstd::is_base_of 仅允许A&lt;T&gt; 其中T 派生自Base,或者如果您只想考虑这个特定的Derived 类型,则直接使用std::is_same

template<typename T>
class A

    template<typename U, typename = std::enable_if_t<std::is_base_of_v<T, U>>>
    A(A<U> const& other);
;

第三,您可以以运算符模板的形式提供隐式转换,方式大致相同。

template<typename T>
class A

    template<typename U, typename = std::enable_if_t<std::is_base_of_v<U, T>>>
    operator U();
;

第四,你可以把f做成一个函数模板,并限制它需要什么类型。

template<typename T, typename = std::enable_if_t<std::is_base_of_v<Base, T>>>
static void f(A<T> const& a);

【讨论】:

以上是关于如何在 C++ 中使用模板类型的子类传递模板函数参数?的主要内容,如果未能解决你的问题,请参考以下文章

模板与泛型编程——模板实参推断

C++ Primer 5th笔记(chap 16 模板和泛型编程)模板实参推断和引用

C++模板非类型模板参数

C++学习35 模板中的函数式参数

[ C++ ] C++之模板template

C++ 模板继承。子类应该用固定类型替换基类中的类型