简体   繁体   中英

How to implement parent class with templated function in c++?

I have a folllowing problem:

I want to implement following structure of classes:

  • Parent in 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 
  • Child in 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 
  • Child in 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(); } 

But when I want to build it, it throws me this error:

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

Any idea what could be wrong? Basically I want to create interface class with templated function which child classes will specify and implement.

Any help would be appreciated. Thanks.

First let's see what the error is trying to tell you, not very eloquently. The error is coming from the linker

error LNK2019: unresolved external symbol

so the compiler was ok with your code, its just that it created a dependency for a symbol that the linker did not find. The symbol is

"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)" blah blah mangled signature ... referenced in function _main

that's not very readable, lets make it more readable by making this substitution

using string = class std::basic_string < char,struct std::char_traits < char >,class std::allocator < char > >

Now the error is

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

What this says is that in function _main you are making a call to the function Parse<string> which is a member of the class Iparser with two parameters, a const reference to a string and a json::Object by value.

But wait, you say, I did provide a definition in my derived class!

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

There are three reasons why this won't work as you intended:

  1. The 2nd parameter is passed by value in the base class member function declaration (json::Object) but you pass by reference in the derived class (json::Object&). Since they have different signature the compiler views this as an "overloaded" version of the base class member function.
  2. If you fix the first error, and declare in the base class that the 2nd parameter is by reference (json::Object&), then the signatures will match, but the linker will still complain because you are trying to call the base class member function, which has not been defined. What you have done is "overridden" the base class Parse member function, so that if you call it using a pointer to the derived class HTMLParser your derived class member function will be called. If you try to call the Parse member function using a pointer to the base class IParser then the compiler generates a call to that function (they are different!) and you haven't defined it. So what do you do in order to make the compiler call the Parse member function of the derived class HTMLParser::Parse when you invoke it using a pointer to the base class IParser ? In order to do that you need to understand polymorphism and virtual inheritance . Ok, you say, I'll make the Parse member function of the IParser base class pure virtual, and force each derived class to provide a definition. That's when you'll run into the third problem.
  3. 'virtual' cannot be specified on member function templates. The reason is templates are resolved at compile time while virtual functions are called dynamically at runtime based on the type associated with the instance (the pointer).

One way to get around this problem of trying to use both generic programming (templates) and object oriented programming (inheritance) is to use a pattern called type erasure, which works in certain cases... you can read more in On the Tension Between Object-Oriented and Generic Programming in C++ and What Type Erasure Can Do About It

Make the whole class a template:

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

Then your child classes can inherit from the templated class:

class HTMLParser : public IParser<std::string>

Note that classes that inherit from different template version will not share a common base class so you may want:

class IParserBase
{
  //...
};

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM