'Forward declare free function in template class

I think I need some tutoring on templates, especially in conjunction with inheritance. I am well aware those two concepts don't play very well together. We've wanted to get ride of clang tidy warnings, but I have no clue how to achieve it.

The abstracted code is below, please see https://godbolt.org/z/sPfx7Yhad to compile it. The setting is, we have some abstract base class (Animal), and a specialized type (Dog). A converter functionality can only be defined on the base class.

There is a templated reader class, which is templated with the actual specialized typed (Reader<Dog>).

However, clang-tidy complains when analyzing reader.h, as converter::convert is not known. It's only known in main.cpp, by including converter.h before reader.h.

I have tried to forward declare the function by using template:

namespace converter
{
  template<typename T>
  void convert(const std::string& input, T& animal);
}

Which leads to linker errors, because now the linker is looking for a void convert(const std::string&, Dog&) implemenation, rather than using the void convert(cons std::string&, Animal&) overload. (see https://godbolt.org/z/x4cPfh6P4)

What can I do? How could I change the design to avoid the clang-tidy warning? In general, I cannot add the actual includes to converter.h in reader.h, as that part is generic and the user shall be able to use the reader with their own types, by providing a custom converter functionality.

What I cannot change are classes Dog and Animal. They are autogenerated classes / libraries which we are using.

For anyone who is interested, the real world example can be found here https://github.com/continental/ecal/blob/master/samples/cpp/measurement/measurement_read/src/measurement_read.cpp

#include <string>
#include <iostream>

// animal.h
// class hierarchy with abstract base class
class Animal
{
public:
   std::string name;
   virtual std::string what() = 0;
};

// dog.h
class Dog : public Animal
{ 
public:
  std::string what() override {return "dog";}
};

// animal_converter.h 
// converting function
// #include <animal.h>
namespace converter 
{
  void convert(const std::string& input, Animal& animal)
  {
    animal.name = input;
  }
}

// reader.h
// Templated class for reader functionality
template <typename T>
class Reader
{
public:
  T read()
  {
    T output;
    converter::convert("Anton", output);
    return output;
  }
};

// main.cpp
// #include dog.h
// #include animal_converter.h
// #include reader.h
int main()
{
  Reader<Dog> reader;
  std::cout << reader.read().name << std::endl;
}


Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source