В общем, первый вариант включает в себя виртуальную диспетчеризацию (т.е. переход к нужной функции во время выполнения), в то время как второй знает правильную функцию, которую нужно вызывать уже во время компиляции. Последний обычно имеет меньше накладных расходов и открывает больше возможностей для оптимизации компилятора, но могут быть и недостатки (размер кода, кэш инструкций и т. Д. c.). Производительность всегда будет зависеть от деталей, поэтому, если вы заботитесь об этом, профиль.
То, что вы не могли сделать с наследованием из коробки, например, возвращает значения различных типов из work_func
- это - то, где шаблоны сияют.
С другой стороны, наследование (особенно при следовании принципу подстановки Лискова) может сделать контракт / ожидания интерфейса намного более ясным (void do_some_templated_work(T &test_class)
не говорит вам, что T
необходимо реализовать, например, print_class_name
).