函数模板:访问子类在基类中的重载函数

时间:2013-08-22 09:58:15

标签: c++ templates

我有一个基类First和一个派生类Second。在基类中有成员函数create和虚函数run。在Second的构造函数中,我想调用函数First::create,该函数需要访问其子类“run()函数的实现”。一位同事建议使用函数模板,因为First无法明确地知道它的子类。听起来怪怪的?这是一些代码:

First.h

#pragma once
#include <boost/thread/thread.hpp>
#include <boost/chrono/chrono.hpp>

class First
{
public:
    First();
    ~First();

    virtual void run() = 0;
    boost::thread* m_Thread;
    void create();

    template< class ChildClass >
    void create()
    {
        First::m_Thread = new boost::thread(
            boost::bind( &ChildClass::run , this ) );
    }
};

First.cpp

#include "First.h"

First::First() {}
First::~First() {}

Second.h

#pragma once
#include "first.h"

class Second : public First
{
public:
    Second();
    ~Second();

    void run();
};

Second.cpp

#include "Second.h"
Second::Second()
{
    First::create<Second>();
}

void Second::run()
{
    doSomething();
}

我在First::create<Second>();收到错误错误:不允许输入类型名称。那么这个错误的原因是什么?我假设我没有完全掌握模板的整体机制,但是 - 我对这个主题很新。

2 个答案:

答案 0 :(得分:1)

首先您必须在标头中放置模板定义,否则链接器将无法找到实例化的模板。 第二:现在双关语已经完成,我假设您从Second派生了First<Second>。这被称为“奇怪的重复模板模式”(CRTP)。

由于基类已经是专用的并且派生类的类型被刻入其类型中,因此不需要将它的任何函数作为模板(您没有),并且基类的函数可以是刚刚调用,没有指定模板参数:

template <class SubClass>
struct First {
  void create() {
    SubClass& sub = static_cast<SubClass&>(*this);
    mThread = make_unique<std::thread>( [&](){sub.run();} ); 
  }
};

struct Second : First<Second> {
  Second() { create(); }
  void run();
};

一些注释:

  • 您无需限定First
  • 内的访问权限
  • 你可以只调用create,它是一个继承的方法,并且由于Second不是模板,所以不需要添加限定条件或帮助编译器进行查找。
  • 更喜欢std::thread而不是boost::thread,现在已经有一段时间了。
  • 更喜欢std::unique_ptr而不是拥有所有权的原始指针
  • 我更喜欢lambdas而不是bind - 对我来说更清楚。
  • 我创建了两个类struct,以便默认情况下将成员函数设置为public,并在示例代码中保存一行。 class除了那个没有区别。

答案 1 :(得分:1)

虽然您可以使用CRre作为Kerrek SB和Arne Mertz的“建议”,但这是使用成员函数模板的解决方案:

class First
{
public:
    First();
    ~First();

    virtual void run() = 0;
    boost::thread* m_Thread;

    // vvvvvvvvvvv this declares a non-template member function `create`
    void create(); // (better remove it)

    // vvvvvvvvvv this declares a member function template `create`
    template< class ChildClass >
    void create();
};

对于模板,您应该在头文件中提供定义,因为定义必须在每个需要它的TU中可用(而不仅仅在一个TU中)。

最简单的方法是在类本身内提供成员函数模板或类模板成员的定义:

class First
{
public:
    /* ... */

    template< class ChildClass >
    void create()
    {
        // AFAIK, the `bind` is not required
        First::m_Thread = new boost::thread(&ChildClass::run,
                                            static_cast<ChildClass*>(this));
    }
};

现在,如果要在Second类中调用此成员函数模板,则必须显式提供模板参数:

void Second::any_function()
{
    create<Second>();
}

您的代码存在其他一些(可能的)问题:

  • 您没有虚拟dtors,因此删除指向基类子对象的指针将调用UB。
  • 由于&ChildClass::run的类型为void (Second::*)(),因此需要进行转发。
  • create的ctor中调用
  • Second,它将调用Second::run(或您提供的任何模板参数),类{em>中的最终重写 {1}} 的。如果您在从Second派生的类中覆盖该函数,则不会在Second的ctor中调用该覆盖。
  • Second的你的dtor应该是Firstdetach提升线程,否则如果在调用dtor时线程仍在运行,则会调用join。< / LI>
  • std::terminate在此示例中不必为虚拟,完全相同的代码可以在run虚拟甚至声明run时使用First