1

我有一个基类First和一个派生类Second。在基类中有一个成员函数create和一个虚函数run。在Second我想调用函数的构造函数中First::create,该函数需要访问其子类run()函数的实现。一位同事建议使用函数模板,因为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 ) );
    }
};

第一个.cpp

#include "First.h"

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

第二个.h

#pragma once
#include "first.h"

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

    void run();
};

第二个.cpp

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

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

我在First::create<Second>();Error: type name is not allowed时遇到错误。那么这个错误的原因是什么?我假设我还没有完全了解模板的全部机制 - 我对这个主题很陌生。

4

2 回答 2

1

尽管您可以按照 Kerrek SB 和 Arne Mertz 的“建议”使用 CRTP,但这里有一个使用成员函数模板的解决方案:

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 中都可用(而不仅仅是一个)。

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

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>();
}

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

  • 您没有虚拟 dtor,因此删除指向基类子对象的指针将调用 UB。
  • 需要有一种沮丧,就像&ChildClass::runtype一样void (Second::*)()
  • create在 的 ctor 内部Second调用,它将调用Second::run(或您提供的任何模板参数),该函数在类中Second的最终覆盖。如果您在派生自 的类中覆盖该函数,Second则不会在 的 ctor 中调用该覆盖Second
  • 您的 dtorFirst应该或者detachboostjoin线程,否则std::terminate如果在调用 dtor 时线程仍在运行,则将被调用。
  • run在此示例中不必虚拟,完全相同的代码可以run在没有虚拟甚至在First.
于 2013-08-22T10:52:32.527 回答
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::threadboost::thread它已经在标准中一段时间​​了。
  • 更喜欢std::unique_ptr拥有所有权的原始指针
  • 我更喜欢 lambdas bind- 我读起来更清楚。
  • 我创建了这两个类struct,以便默认情况下公开成员函数并在示例代码中保存一行。除此之外没有任何区别class
于 2013-08-22T10:20:57.253 回答