2

I have a Calculator<T> class and a LoggingCalculator<T> class, which inherites from Calculator<T>. Calculator<T> has a virtual methods like add, multiply etc. In a child class LoggingCalculator<T> I am trying to override these methods. I have already overrided the add method, fine. But I cannot override multiply, because compiler says:

Calculator::Calculator[int]::Calculator(void)': no suitable definition provided for explicit template instantiation request

even though, I explicitly instantiated my templates. I really do not understand, why this error occures with overriding multiply method, while add compiles fine.

The really weird thing is that it does compile, if I do not return generic value returned from base class method. But If want to return result from the base class method, it does not compile. See LoggingCalculator.cpp code below.

Calculator.h:

template <class T>
class Calculator
{
public:
    Calculator();
    ~Calculator();
    virtual T add(T a, T b);
    virtual T multiply(T a, T b);
}
template class Calculator<int>;
template class Calculator<long>;
template class Calculator<float>;
template class Calculator<double>;

Calculator.cpp:

template<class T>
Calculator<T>::Calculator() {}

template<class T>
Calculator<T>::~Calculator() {}

template<class T>
T Calculator<T>::add(T a, T b)
{
    return a + b;
}

template<class T>
T Calculator<T>::multiply(T a, T b)
{
    return a * b;
}

LoggingCalculator.h:

template <class T>
class LoggingCalculator : public Calculator<T>
{
public:
    LoggingCalculator();
    ~LoggingCalculator();
    T add(T a, T b) override;
    T multiply(T a, T b) override;
};

template class LoggingCalculator<int>;
template class LoggingCalculator<long>;
template class LoggingCalculator<float>;
template class LoggingCalculator<double>;

LoggingCalculator.cpp:

template<class T>
LoggingCalculator<T>::LoggingCalculator() { }

template<class T>
LoggingCalculator<T>::~LoggingCalculator() {}

template<class T>
T LoggingCalculator<T>::add(T a, T b)
{
    T result = Calculator<T>::add(a, b);
    return result;
}

template<class T>
T LoggingCalculator<T>::multiply(T a, T b)
{
    T result =  Calculator<T>::multiply(a, b);
    //return a * b; // COMPILES FINE!
    return result // CAUSES ERROR
}
Dariusz
  • 610
  • 1
  • 6
  • 14
  • 1
    My God, I have added semicolon after `return result` and now it compiles. Seems like it was just a syntax error. – Dariusz May 26 '18 at 13:40

1 Answers1

3

You'll have to move the explicit instantiations to their respective source files. That's because definitions of what you're instantiating need to be accessible at the point of instantiation.

What you're doing now instantiates the definition of the class Calculator<int> and with it, the declaration of Calculator<int>::add, but it does not instantiate the definition of Calculator<int>::add, because the definition is not available.

And since explicit template instantation is a definition, it should normally not appear in a header file anyway — that would likely lead to multiple-definition issues down the line.

Angew is no longer proud of SO
  • 167,307
  • 17
  • 350
  • 455
  • Still, why would add work fine? I'm curious, otherwise definitely liked your answer. – atru May 26 '18 at 13:31
  • Maybe I will like your answer, but how do you explain this? I added semicolon after `return result` and now it compiles. It seems like it was just a syntax error... – Dariusz May 26 '18 at 13:38
  • @Darko That it's working for you by pure chance. Being in the header, the `Calculator` instantiations appear in both translation units (= preprocessed source files), but only one of them has the definitions at hand to actually instantiate them. Still, the instantiations do *not* belong in the header, semantically. – Angew is no longer proud of SO May 26 '18 at 13:52
  • @Darko don't include cpp files. – Guillaume Racicot May 26 '18 at 13:52
  • @Angew So where is the right place to put explicit template instantiation? Or how I should specify allowed types for the template class? I am relatively new in c++ and I am not familiar with templates in c++. I put the explicit template instantiation in the header, following some answer on stackoverflow. – Dariusz May 26 '18 at 18:48
  • @Darko See the answer's first sentence: "You'll have to move the explicit instantiations to their respective source files." In general, each explicit instantiation should exist *exactly once* in the program, and it needs to be in a translation unit which contains all the template definitions needed by the instantiation. – Angew is no longer proud of SO May 26 '18 at 18:50
  • @Angew My intuition says, that `Calculator::add` definition will be instantiated in my code. The definitions of all methods should be provided, because I specified the definitions of generic methods in a Calculator.cpp and T will be replaced with int, since I instantiated the definition of the class Calculator in the header. It works now, so probably I am right (?). But there can be misconceptions in my thinking and I should definitely read a good text about templates in c++ :) – Dariusz May 26 '18 at 19:04
  • 1
    @Darko Find a [good book in our list](https://stackoverflow.com/q/388242/1782465) :-) But the thing is that since the instatiation is in the header, it will *also* appear in preprocessed `LogicCalculator.cpp`. And that one does *not* see the definitions from `Calculator.cpp` (remember object files are independent of each other!). – Angew is no longer proud of SO May 26 '18 at 19:15
  • @Angew Thanks, now it is clearer for me. Did you mean `LoggingCalculator.cpp` instead of `LogicCalculator.cpp`? – Dariusz May 26 '18 at 19:27