首页 文章

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

提问于
浏览
1

我有一个基类 First 和一个派生类 Second . 在基类中有一个成员函数 create 和一个虚函数 run . 在 Second 的构造函数中,我想调用函数 First::create ,它需要访问其子类' run() 函数的实现 . 一位同事建议使用函数模板,因为 First 可以't know it'明确地显示子类 . 听起来怪怪的?这是一些代码:

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>(); 收到错误,说错误:不允许输入类型名称 . 那么什么's the reason for this error? I assume I didn' t得到模板的整体机制,但是 - 我对这个话题很新 .

2 回答

  • 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::*)() .
      Second 的ctor中调用

    • create ,它将调用 Second::run (或您提供的任何模板参数),该类函数在 Second 中的最终覆盖 . 如果在从 Second 派生的类中覆盖该函数,则不会在 Second 的ctor中调用该覆盖 .

    • First 的dtor应该是 detachjoin 提升线程,否则如果在调用dtor时线程仍在运行,则会调用 std::terminate .

    • run 在此示例中不必为虚拟,完全相同的代码可以在 First 中虚拟或甚至声明为 run .

  • 1

    首先,您必须在标头中放置模板定义,否则链接器将无法找到实例化的模板 . 第二:现在双关语已经完成了,我假设你从 First<Second> 派生了 Second . 这称为"Curiously Recurring Template Pattern"(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 没有区别 .

相关问题