【问题标题】:C++ inheritance in template parameter模板参数中的 C++ 继承
【发布时间】:2016-06-04 23:13:34
【问题描述】:

我需要将模板作为函数参数传递,但模板必须接受所有继承的对象。

例如:

template <class T>
class Template {
public:
    Template() {}
};

// IMPORTANT: must accept all children of "Base"
class Test {
    Template<Base> *pointer;
public:
    Test(Template<Base> *pointer) {...}
};


main() {
    Template<Base> *base = new Template<Base>();
    Template<Child1> *child = new Template<Child1>();

    Test *test1 = new Test(base);    // WORKING
    // NEXT LINE IS IMPORTANT:
    Test *test2 = new Test(child);   // ERROR: no matching function for call to 'Test::Test(Template<Child1>*&)'
}


class Base {...};
class Child1 : public Base {...};
class Child2 : public Base {...};
..

我需要最有效的方法,如何将模板存储在“测试”类中,它将接受所有子类。我有很多儿童班。

1) 是否可以在参数中为 Child 类进行一些强制转换(static_cast)?在“模板”或“测试”类中使用它会很好。不是,在创建新的“测试”对象时,因为会有很多新对象。

2) 或者传递不带模板参数的参数Test(Template *pointer) {...}?

或者如何解决?

【问题讨论】:

  • Template&lt;Child1&gt; 与Template&lt;Base&gt; 完全无关。他们是完全不同的班级。根据您要执行的操作,类型擦除可能是一个可行的选择。
  • 有什么阻止您将基类(非模板化)添加到 Template 并将其存储在您的测试类中吗?
  • @coyotte508:是的,可以接受的解决方案,但我想知道,是否有模板参数继承的直接解决方案。
  • @Cornstalks:谢谢,会尝试类型擦除

标签: c++ templates inheritance parameters parameter-passing


【解决方案1】:

你想做的事情不起作用的原因是因为模板类型不遵守类型名参数中的继承。

Template<Base> *base

和

Template<Child1> *child

是完全不同的类型。尝试使用一个代替另一个就像尝试将 int 传递给具有 string 参数的函数。

也就是说,我相信您在这里有两个可行的选择。

#1 也将Test 设为模板

一种选择是将Test 也设为模板:

template <typename T>
class Test {
    T *pointer;
public:
    Test(T *pointer) {...}
};

这将允许Test 对象与传递给它的任何对象一起工作。

#2 使用接口

或者,通过创建一个接口并在Template 中实现该接口,您可以将您的接口传递给Test 类:

/**
 * Interface that declares the virtual methods that
 * will be used by your Test class.
 */
class ITemplate {
public:
    virtual ~ITemplate() {}

    // Define your methods here.
    virtual void Foo() = 0;
    virtual void Bar() = 0;
    virtual void Baz() = 0;
}

/**
 * Concrete ITemplate implementation.
 */
template <class T>
class Template : public ITemplate {
public:
    Template() {}
    virtual ~Template() {}

    virtual void Foo() override {}
    virtual void Bar() override {}
    virtual void Baz() override {}

};

class Test {
    ITemplate *pointer;
public:
    Test(ITemplate *pointer) {...}
};

这样做的好处是您可以修改您的Template 类而不影响您的Test 类,因为Test 类使用ITemplate 接口而不是具体对象。这样做的缺点是会产生虚函数查找的成本,如果您尝试编写极其高效的代码,这可能是不可取的。

【讨论】:

  • 我之前尝试过第一个解决方案,但你必须指定模板类型 2 次 - 用于模板和测试类。我想知道任何更好的解决方案。如果岸上时间没有更多的解决方案,我会接受接口解决方案。谢谢!
  • 我有一个接口解决方案的问题。如何根据类型使用方法?例如,我在“模板”类中有:void Foo(T) override {},我该如何为此制作接口?我在 ITemplate 中没有:virtual void Foo(Base) = 0;。在模板类中:T Foo() override {} 和 ITemplate:virtual Base Foo() = 0;
  • @Nick - 我不认为你可以用界面做你想做的事。 T 将是未定义的,除非您将 Foo 设为模板函数,但您不能拥有虚拟模板函数。
【解决方案2】:

1) 是否可以在参数中为 Child 类进行一些强制转换(static_cast)?在“模板”或“测试”类中使用它会很好。不是,在创建新的“测试”对象时,因为会有很多新对象。

我不完全明白你的建议。不可能将不相关的指针转换为另一个指针。

2) 或者不带模板参数传递参数

没有。模板不能是函数参数。

您可以做的是让Template&lt;T&gt; 隐式转换为Template 的其他实例:

template <class T>
class Template {
public:
    // ...

    template<class U>
    Template(const Template<U>&);
};

根据转换构造函数的实现方式,可能会对U施加限制。

此外,如果您要在 Test 中存储 Template&lt;Base&gt; 实例而不是指针:

class Test {
    Template<Base> member;
    // ...
};

然后,您可以通过值(复制)或右值引用(移动)将Template&lt;Base&gt; 接受到构造函数。 Template&lt;Child&gt; 将隐式转换为 Template&lt;Base&gt;:

class Test {
    Template<Base> member;
public:
    Test(Template<Base> argument) {/* ... */}
};

这种方法与允许隐式向上转换标准智能指针类型的方法相同。

【讨论】:

  • 谢谢,这是一个有趣的解决方案,只有一个问题是,它将在 Test 对象中创建 Template 的副本。如果有指针(或移动)类似的解决方案,那就好了。
  • @Nick 您应该考虑创建Template&lt;Base&gt; 的副本是否是一件坏事,如果是,那么为什么会这样。如果您想移动,那么使用Test(Template&lt;Base&gt;&amp;&amp; argument) 肯定是可能的。存储指针不是一个选项,因为隐式转换的 Template&lt;Base&gt; 是一个临时的,将在对构造函数的调用结束后被销毁,因此指向它的指针将悬空。
猜你喜欢
  • 2021-10-17
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-07-15
  • 1970-01-01
  • 2015-11-10
  • 2018-11-11
相关资源
最近更新 更多