Прочитав много постов на похожие темы и немного подумав, я все равно не понимаю, почему запрещено реализовывать виртуальные функции шаблона.
На мой взгляд, этот случай не имеет ничего общего со смешиванием статического полиморфизма с динамическим, но он скорее использует шаблонную дифференциацию функций во время компиляции, а затем использует динамический полиморфизм для каждой отдельной созданной функции во время выполнения. время.
Рассмотрим этот кусок кода:
class parrent{
public:
virtual float function(float value)const{
return value;
}
virtual double function(double value)const{
return value;
}
virtual long double function(long double value)const{
return value;
}
virtual ~parrent() = default;
};
class a_child:public parrent{
public:
float function(float value)const override{
return value + 1.5;
}
double function(double value)const override{
return value + 1.5;
}
long double function(long double value)const override{
return value + 1.5;
}
};
Очевидно, что этот код в порядке и достигнет ожидаемого результата.
Но с помощью шаблона переписать похожий код:
class parrent{
public:
template<typename t__>
virtual t__ function(t__ value)const{
return value;
}
virtual ~parrent() = default;
};
class a_child:public parrent{
public:
template<typename t__>
t__ function(t__ value)const override{
return value + 1.5;
}
};
Не допускается.
Я не дизайнер компиляторов, но из того, что я прочитал, компиляторы создадут справочную таблицу из виртуальных функций и используют их для запуска соответствующей функции во время выполнения, что отличается от того, что они делают в случае функций шаблона , Для любых наборов параметров шаблона, заданных для использования функции шаблона во время компиляции, компилятор создаст уникальную функцию.
В этом примере компилятор может обнаружить параметры шаблона во время компиляции, просто взглянув на то, как эта функция виртуального шаблона использовалась во всей программе. Пожалуйста, рассмотрите основную функцию сейчас:
int main() {
parrent* a;
parrent* b;
a = new parrent;
b = new a_child;
std::cout<< a->function(1.6f) << std::endl;
std::cout<< a->function(1.6) << std::endl;
std::cout<< a->function(1.6L) << std::endl;
std::cout<< b->function(1.6f) << std::endl;
std::cout<< b->function(1.6) << std::endl;
std::cout<< b->function(1.6L) << std::endl;
delete a;
delete b;
return 0;
}
Здесь компилятор увидит, что функция использовалась один раз для значения с плавающей запятой, один раз для двойного значения и один раз для длинного двойного значения, поэтому в любом случае он может легко создать правильную функцию с соответствующими параметрами шаблона.
И, наконец, будет 3 отдельных виртуальных функции, а не только одна виртуальная функция.
Если у нас есть функция, параметры шаблона которой не могут быть выведены из входных данных функций, таких как
template<typename t__>
virtual t__ function(int value){return value;}
Тогда пользователи могут сами задать параметры, например:
object_pointer->function<double>(1234);
Эти методы - то, что уже используется в случае каких-либо шаблонных функций, так почему они могут отличаться для виртуальных функций!
единственное предостережение к этой практике, о котором я могу думать, это когда экземпляр виртуальной функции шаблона создается из дочернего объекта, а не из родительского объекта или указателя.
Ну, даже в этом случае такая же практика может быть применена для создания различных виртуальных функций. В качестве альтернативы из-за недостатка использования их виртуальности они могут стать нормальными индивидуальными функциями.
Из ответа и комментариев видно, что с этим подходом может возникнуть серьезная проблема, которая очевидна для всех остальных, поэтому, пожалуйста, наберитесь терпения и помогите мне понять это тоже.
Я полагаю, что упомянутая проблема в ответах связана с тем, что компилятор и / или компоновщик не могут знать, сколько (и какого типа) таблиц он должен создать для класса в отношении остальных кодов или других единицы перевода, с которыми он может столкнуться.
Хорошо, допустим, что он может создать незаконченный список таблиц и расширять его по мере продвижения. Проблема получения двух виртуальных таблиц или двух разных экземпляров одного и того же класса в случае динамического связывания уже может возникнуть при создании экземпляра класса шаблона с помощью виртуальной (не шаблонной) функции.
Похоже, у компиляторов уже есть способ обойти эту проблему!
Во-первых, давайте не будем забывать, что в отношении c методы или классовые нестатические функции являются не чем иным, как простыми функциями, для которых объект является одним из их параметров, поэтому давайте не будем думать о классе как о каком-то сложном фрагменте кода.
Во-вторых, давайте не будем увлекаться тем, как компиляторы и компоновщики и что не работает сегодня. Язык должен быть стандартным, а не так, как компиляторы создают исполняемый файл! Давайте не будем забывать, что в стандарте c ++ 17 все еще есть много функций, которые даже GCC не охватывает!
Пожалуйста, объясните мне с точки зрения логики, как работают компиляторы и / или компоновщики, в чем проблема?