Метапрограммирование - довольно экзотическая тема. Это интересно узнать, это мощный инструмент, и иногда вы можете найти его полезным. Но это никогда не будет самым часто используемым инструментом в вашем наборе инструментов. Иногда вы можете захотеть, чтобы ваш код воздействовал на ряд несвязанных типов с различными свойствами, и вот тут-то и возникает метапрограммирование. С небольшой хитростью вы можете написать перегрузку функции, которая доступна, только если тип аргумента является целым или если это указатель или тип X, Y или Z (возможно, игнорируя постоянство на Z).
Это по сути программирование с типами. Обычно ваша программа может делать такие вещи, как брать два числа и производить третье число, или сообщать вам, удовлетворяет ли число некоторому требованию. Метапрограммирование может принимать два типа и создавать третий тип, или сообщать вам, удовлетворяет ли тип некоторому требованию. И да, это, вероятно, в основном полезно при разработке библиотеки. Но опять же, большая часть кода может считаться библиотечным кодом. Вы можете сказать, что все, что находится за пределами вашей функции main (), является библиотечным кодом.
Обычно, если вы хотите решить проблему с помощью метапрограммирования, вы, вероятно, захотите использовать соответствующие библиотеки повышения для выполнения тяжелой работы. Boost.TypeTraits и, конечно, Boost.Mpl могут действительно упростить для вас вещи. Но это не то, что вам нужно , чтобы знать, и это не то, что вам, вероятно, понадобится очень часто.
Общее программирование связано (и может в некоторых случаях использовать метапрограммирование под капотом, чтобы стать действительно универсальным, например, стандартная библиотека использует прикосновение метапрограммирования, чтобы превратить необработанные указатели в действительные итераторы, что требуется для концепции «итератора»). быть универсальным), но не совсем то же самое. И это гораздо более широко используется.
Каждый раз, когда вы создаете экземпляр std::vector
, вы используете общее программирование. Каждый раз, когда вы используете пару итераторов для обработки последовательности значений, вы используете общее программирование. Универсальное программирование - это просто идея, что ваш код должен быть настолько универсальным, насколько это возможно, и должен работать независимо от того, какие типы включены в него. Std :: vector не требует, чтобы содержащийся тип реализовывал интерфейс «ICanBeContained» (помните, как Java требует, чтобы все было извлечено из Object для того, чтобы оно было сохранено в классе контейнера? Что означает, что примитивные типы упаковываются в блоки, и что мы теряем безопасность типов. Это не общее и бессмысленное ограничение.)
Код для итерации последовательности с использованием итераторов является общим и работает с итераторами любого типа или даже с простыми указателями.
Общее программирование очень широко полезно и часто может в значительной степени заменить ООП. (см. приведенный выше пример. Зачем мне писать контейнер, в котором требуются содержащиеся типы для реализации интерфейса, если я могу избежать этого ограничения?)
Часто, когда вы используете интерфейсы в ООП, это не означает, что тип может изменяться во время выполнения (хотя, конечно, это происходит время от времени), но позволяет вам переключаться на другой тип во время компиляции ( возможно, введение фиктивного объекта во время тестов, а не использование полноценной реализации) или просто для разделения двух классов. Универсальное программирование может сделать это без необходимости выполнять утомительную работу по определению и поддержке интерфейса. В этих случаях универсальное программирование означает, что вам нужно писать и поддерживать меньше кода, и вы получаете лучшую производительность и лучшую безопасность типов. Так что да, вы обязательно должны чувствовать себя как дома с общим программированием. C ++ не очень хороший язык ООП. Если вы хотите строго придерживаться ООП, вам следует переключиться на Java или другой, более ориентированный на ООП язык. C ++ позволяет писать OO-код, но зачастую это не лучшее решение. Есть причина, по которой почти вся стандартная библиотека опирается на общее программирование, а не на ООП. В стандартной библиотеке очень мало наследования или полиморфизма. Им это не нужно, и код стал проще и мощнее без него.
И чтобы ответить на другие ваши вопросы, да, общее программирование - это в значительной степени отдельная парадигма. Шаблон метапрограммирования нет. Это довольно специфический метод для манипулирования системой типов, и он очень хорош при решении небольшого числа проблем. Чтобы считаться парадигмой, я думаю, что она должна быть гораздо более полезной, и подход, который вы можете использовать для всего , например, функционального, ОО или общего программирования.
Я думаю, что xtofl действительно прибил это: общее программирование - это то, что ваш код не знает типа. (Std :: vector не заботится , или не нужно знать , какой тип хранится в нем. Это просто работает.)
С другой стороны, метапрограммирование связано с вычислениями типов. Для типов T0 и T1 мы можем определить тип T2, точно так же, как мы можем, для целых чисел N0 и N1 мы можем определить N2, который является суммой N0 и N1.
Библиотека Boost.Mpl имеет очевидный пример этого.
В вашем обычном коде, если у вас есть целые числа N0, N1 и N2, вы можете создать std :: vector, содержащий эти три значения. Затем я могу использовать другой алгоритм для вычисления индекса, а затем извлечь значение, хранящееся в этом месте в векторе.
Учитывая типы T0, T1 и T2, мы можем создать mpl :: vector, содержащий эти три типа .
Теперь я могу использовать другой алгоритм для вычисления индекса во время компиляции и извлечения типа , хранящегося в этом месте в векторе.