Мда... Ну давайте разжуём про интерфейс - реализацию.
понял вопрос.
рассмотрим класс:
class ISomeClass
{
template <class T>
virtual void doSomething(T value) = 0;
}
сам по себе шаблон-виртуал ничего не значит,
если нет инстанций.
допустим в нашей библиотечке есть класс:
C++ (Qt)
#include <ISomeClass.h>
class SomeConcreteClass : public ISomeClass
{
template <class T>
void doSomething(T value) override
{
std::cout << "SomeConcreteClass value = " << value << std::endl;
}
}
его реализация так же ничего не значит до тех пор,
пока нет инстанций.
но вот тут компилятор видит вызовы шаблоно-виртуалов:
C++ (Qt)
ISomeClass * some_object = makeSomeObject();
some_object->doSomething(int(10));
some_object->doSomething(double(5.7));
шаблоно-виртуал - на самом деле не более чем сахар
для автоматической копипасты виртуальных функций-членов.
и такое инстанцирование аналогично тому,
как если бы мы вручную сделали:
class ISomeClass
{
virtual void doSomething(int value) = 0;
virtual void doSomething(double value) = 0;
}
class SomeConcreteClass : public ISomeClass
{
void doSomething(int value) override
{
std::cout << "SomeConcreteClass value = " << value << std::endl;
}
void doSomething(double value) override
{
std::cout << "SomeConcreteClass value = " << value << std::endl;
}
}
то бишь компилятор генерирует самый обычный vtbl,
для двух методов.
сама библиотека содержит реализации самых обычных
виртуальных методов.
именно такой подход я имел ввиду,
когда выше писал:
"шаблоно-виртуал - не более чем просто сахарок,
который реализует автоматическую копипасту"
в действительности, мы могли бы вручную
по старинке написать пару чисто-виртуальных функций,
и класс с реализацией. получилось бы все ровно тоже самое.
интересное дальше:
при компиляции итогового приложения,
компилятор видит вызов метода с ещё одним параметром:
some_object->doSomething(new QWidget());
соответственно, он инстанцирует шаблоно-виртуал под него.
для этого ему нужно расширить vtbl базового класса,
как если бы он был:
class ISomeClass
{
virtual void doSomething(int value) = 0;
virtual void doSomething(double value) = 0;
virtual void doSomething(QWidget* value) = 0;
}
поскольку библиотечный класс-наследник
не содержит реализации нового метода,
то сама попытка вызвать для него этот метод
приведет к ошибке времени выполнения.
здесь важно подчеркнуть:
именно вот такая механика,
именно вот таким образом
(за искл. возможности расширения vtbl базового класса)
используется в настоящий моменти без всяких этих ваших шаблоно-виртуалов.
в действительности,
дизайн полиморфизма с++ с его классовой системой
просто технически не способны защитить от подобных ошибок:
http://rextester.com/LVWMK76234#include <iostream>
struct base
{
virtual void doSomething(int value) = 0;
base* sample = nullptr; // <--- regular in real code
};
struct concrete: base
{
~concrete()
{
sample->doSomething(10); // <--- upps
}
};
struct der: concrete
{
der()
{
this->sample = this; // bomb
}
virtual void doSomething(int value)
{
std::cout <<"doSomething("<<value<<")\n";
}
};
int main()
{
der d; //<--- babah!!!
}
при этом так же нельзя не заметить,
что несмотря на потенциальную возможность,
на практике подобные ошибки редки.
(я только у новичков встречал, например)
просто потому, что как правило программисты - ребята достаточно здравомыслящие,
и не пишут откровенное УГ за гранью здравого смысла.
выдавать наружу интерфейс на внутреннего наследника,
содержащего шаблонно-виртуал - ССЗБ.