【问题标题】:How to implement parent class with templated function in c++?如何在 C++ 中使用模板函数实现父类?
【发布时间】:2014-11-06 16:19:03
【问题描述】:

我有以下问题:

我想实现以下类结构:

  • IParser.h 中的父级

    #ifndef IPARSER_H
    #define IPARSER_H
    
    #include "json.h"
    
    class IParser
    {
    public:
        template <typename T> 
        json::Object Parse(const T&, json::Object);
    };
    
    #endif // IPARSER_H
    
  • HTMLParser.h中的孩子

    #ifndef HTMLPARSER_H
    #define HTMLPARSER_H
    
    #include <iostream>
    
    #include "IParser.h"
    
    class HTMLParser : public IParser
    {
    public:
        HTMLParser();
        ~HTMLParser();
    
        json::Object Parse(std::string const&, json::Object&);
    };
    
    #endif
    
  • HTMLParser.cpp中的孩子

    #include "HTMLParser.h"
    
    HTMLParser::HTMLParser()
    {
        std::cout << "constructed" << std::endl;
    }
    
    HTMLParser::~HTMLParser()
    {
        std::cout << "destructed" << std::endl;
    }
    
    json::Object HTMLParser::Parse(std::string const& data, json::Object& object)
    {
        // do something
        return json::Object();
    }
    

但是当我想构建它时,它会抛出这个错误:

error LNK2019: unresolved external symbol "public: class json::Object __thiscall
IParser::Parse<class std::basic_string<char,struct std::char_traits<char>,class
std::allocator<char> > >(class std::basic_string<char,struct std::char_traits<char>,class
std::allocator<char> > const &,class json::Object)" (??$Parse@V?$basic_string@DU?
$char_traits@D@std@@V?$allocator@D@2@@std@@@IParser@@QAE?AVObject@json@@ABV?$basic_string@DU?
$char_traits@D@std@@V?$allocator@D@2@@std@@V12@@Z) referenced in function _main

知道有什么问题吗? 基本上我想创建带有模板函数的接口类,子类将指定和实现。

任何帮助将不胜感激。谢谢。

【问题讨论】:

  • 模板方法不能是虚拟的。
  • 所以我不能定义一个具有不同第一个参数的方法,哪些孩子将使用特定类型实现?

标签: c++ templates inheritance interface


【解决方案1】:

首先让我们看看错误试图告诉你什么,不是很雄辩。错误来自链接器

error LNK2019: unresolved external symbol

所以编译器对您的代码没有问题,只是它为链接器未找到的符号创建了依赖关系。符号是

"public: class json::Object __thiscall IParser::Parse,class std::allocator > >(class std::basic_string ,class std::allocator > const &,class json::Object)" blah blah mangled signature ... 在函数 _main 中引用

这不是很可读,让我们通过这个替换使其更具可读性

使用 string = class std::basic_string ,class std::allocator >

现在错误是

"public:class json::Object __thiscall IParser::Parse(class string const &, class json::Object)"

这就是说,在函数 _main 中,您正在调用函数 Parse&lt;string&gt;,它是 Iparser 类的成员,带有两个参数,一个对字符串的 const 引用和一个 json::Object 值.

但是等等,你说,我确实在派生类中提供了一个定义!

json::Object HTMLParser::Parse(std::string const& data, json::Object& object)
{
    // do something
    return json::Object();
}

这无法按您的预期工作的三个原因:

  1. 第二个参数在基类成员函数声明 (json::Object) 中通过值传递,但在派生类 (json::Object&) 中通过引用传递。由于它们具有不同的签名,编译器将其视为基类成员函数的“重载”版本。
  2. 如果您修复第一个错误,并在基类中声明第二个参数是通过引用 (json::Object&),则签名将匹配,但链接器仍会抱怨,因为您正在尝试调用基类类成员函数,尚未定义。您所做的是“覆盖”了基类Parse 成员函数,因此如果您使用指向派生类HTMLParser 的指针调用它,您的派生类成员函数将被调用。如果您尝试使用指向基类IParser 的指针调用Parse 成员函数,那么编译器会生成对该函数的调用(它们是不同的!)并且您还没有定义它。那么,当您使用指向基类IParser 的指针调用派生类HTMLParser::ParseParse 成员函数时,您要怎么做呢?为此,您需要了解polymorphism and virtual inheritance。好吧,你说,我将IParser基类的Parse成员函数设为纯虚函数,并强制每个派生类提供定义。这时候你会遇到第三个问题。
  3. 不能在成员函数模板上指定“虚拟”。原因是模板在编译时解析,而虚函数是 called dynamically at runtime 基于与实例关联的类型(指针)。

解决这个尝试同时使用泛型编程(模板)和面向对象编程(继承)的问题的一种方法是使用一种称为类型擦除的模式,该模式在某些情况下有效...您可以在 On the Tension Between Object-Oriented and Generic Programming in C++ and What Type Erasure Can Do About It

【讨论】:

  • 感谢您详尽的回复,我更正了您提到的前两个问题和第三个问题,所以我会按照您的建议查看type erasurething。谢谢!
  • 如您所见,@Jarod42 的评论是正确的,我们只需要解决一些问题就可以了。类型擦除可能对您的问题来说太大了,您必须决定是否需要这种级别的灵活性。
【解决方案2】:

让整个类成为模板:

template <typename T> 
class IParser
{
public:
    json::Object Parse(const T&, json::Object);
};

那么你的子类可以继承模板类:

class HTMLParser : public IParser<std::string>

请注意,从不同模板版本继承的类不会共享一个公共基类,因此您可能需要:

class IParserBase
{
  //...
};

template <typename T> 
class IParser : public IParserBase
{
public:
    json::Object Parse(const T&, json::Object);
};

【讨论】:

  • 我想避免创建 templated class 因为在主函数中我只想创建一个 IParser 类型的变量并基于子类的输入创建对象,例如:IParser* parser = new HTMLParser()IParser* parser = new XMLParser() 等等。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-06-12
  • 2020-05-30
  • 1970-01-01
  • 1970-01-01
  • 2014-11-02
  • 2014-09-01
  • 1970-01-01
相关资源
最近更新 更多