如何在c ++中用模板函数实现父类?

时间:2014-11-06 16:19:04

标签: c++ templates inheritance interface

我有一个以下问题:

我想实现以下类的结构:

  • 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

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

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

2 个答案:

答案 0 :(得分:1)

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

error LNK2019: unresolved external symbol

所以编译器对你的代码没问题,只是它为链接器找不到的符号创建了一个依赖项。符号是

  

“public:class json :: Object __thiscall IParser :: Parse&lt; class std :: basic_string&lt; char,struct std :: char_traits&lt; char&gt ;, class std :: allocator&lt; char&gt;&gt;&gt; ;(类std :: basic_string&lt; char,struct std :: char_traits&lt; char&gt;,class std :: allocator&lt; char&gt;&gt; const&amp;,class json :: Object)“blah blah mangled signature。 ..在函数_main

中引用

这不是非常易读,让我们通过这种替换使其更具可读性

  

使用string = class std :: basic_string&lt; char,struct std :: char_traits&lt; char&gt;,类std :: allocator&lt; char&gt; &GT;

现在错误是

  

“public:class json :: Object __thiscall IParser :: Parse&lt; string&gt;(class string const&amp;,class json :: Object)”

这就是说在函数_main中你正在调用函数Parse<string>,它是类Iparser的成员,有两个参数,一个对字符串的引用和一个json: :按值对象。

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

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

这有三个原因导致它无法按预期运行:

  1. 第二个参数在基类成员函数声明(json :: Object)中通过值传递,但是您在派生类(json :: Object&amp;)中通过引用传递。由于它们具有不同的签名,因此编译器将其视为基类成员函数的“重载”版本。
  2. 如果您修复了第一个错误,并在基类中声明第二个参数是引用(json :: Object&amp;),那么签名将匹配,但链接器仍会抱怨,因为您试图调用基类成员函数,尚未定义。你所做的是“覆盖”基类Parse成员函数,这样如果你使用指向派生类HTMLParser的指针调用它,你的派生类成员函数将被调用。如果您尝试使用指向基类Parse的指针调用IParser成员函数,则编译器会生成对该函数的调用(它们不同!)并且您尚未定义它。那么当你使用指向基类Parse的指针调用它时,你怎么做才能让编译器调用派生类HTMLParser::Parse的{​​{1}}成员函数?为此,您需要了解polymorphism and virtual inheritance。好吧,你说,我将使IParser基类的Parse成员函数为纯虚拟,并强制每个派生类提供一个定义。那是你遇到第三个问题的时候。
  3. 'virtual'无法在成员函数模板上指定。原因是模板在编译时解析,而虚函数是called dynamically at runtime,基于与实例关联的类型(指针)。
  4. 解决尝试使用泛型编程(模板)和面向对象编程(继承)这一问题的一种方法是使用一种称为类型擦除的模式,它在某些情况下有用...你可以在<强> On the Tension Between Object-Oriented and Generic Programming in C++ and What Type Erasure Can Do About It

答案 1 :(得分:0)

让整个班级成为模板:

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