Являются ли шаблоны C ++ просто замаскированными макросами? - PullRequest
53 голосов
/ 08 октября 2008

Я программировал на C ++ в течение нескольких лет, и я довольно часто использовал STL и несколько раз создавал свои собственные классы шаблонов, чтобы посмотреть, как это делается.

Теперь я пытаюсь глубже интегрировать шаблоны в мой ОО-проект, и мне все время возвращается мучительная мысль: на самом деле это всего лишь макросы ... Вы можете реализовать (скорее УГЛЫЙ) auto_ptrs, используя #defines, если ты действительно хотел.

Такой подход к шаблонам помогает мне понять, как на самом деле будет работать мой код, но я чувствую, что, должно быть, как-то упускаю суть. Макросы означают воплощение зла, но «шаблонное метапрограммирование» - все в моде.

Итак, каковы реальные различия? и как шаблоны могут избежать опасностей, к которым приводит вас #define, например

  • Непостижимые ошибки компилятора в места, где вы их не ожидаете?
  • Раздувание кода?
  • Сложность в трассировке кода?
  • Установка точек останова отладчика?

Ответы [ 25 ]

51 голосов
/ 08 октября 2008

Макросы - это механизм подстановки текста.

Шаблоны - это функциональный язык, полный тьюринга, который выполняется во время компиляции и интегрирован в систему типов C ++. Вы можете думать о них как о механизме плагинов для языка.

30 голосов
/ 08 октября 2008

Здесь много комментариев, пытающихся различить макросы и шаблоны.

Да - это одно и то же: инструменты генерации кода.

Макросы - это примитивная форма, без особого применения компилятором (например, выполнение объектов в C - это может быть сделано, но это не красиво). Шаблоны более продвинуты и имеют намного лучшую проверку типов компиляторов, сообщения об ошибках и т. Д.

Однако у каждого есть сильные стороны, которых нет у другого.

Шаблоны могут генерировать только динамические типы классов - макросы могут генерировать практически любой код, который вы хотите (кроме другого определения макроса). Макросы могут быть очень полезны для встраивания статических таблиц структурированных данных в ваш код.

Шаблоны, с другой стороны, могут выполнять действительно СМЕШНЫЕ вещи, которые невозможны с макросами. Например:

template<int d,int t> class Unit
{
    double value;
public:
    Unit(double n)
    {
        value = n;
    }
    Unit<d,t> operator+(Unit<d,t> n)
    {
        return Unit<d,t>(value + n.value);
    }
    Unit<d,t> operator-(Unit<d,t> n)
    {
        return Unit<d,t>(value - n.value);
    }
    Unit<d,t> operator*(double n)
    {
        return Unit<d,t>(value * n);
    }
    Unit<d,t> operator/(double n)
    {
        return Unit<d,t>(value / n);
    }
    Unit<d+d2,t+t2> operator*(Unit<d2,t2> n)
    {
        return Unit<d+d2,t+t2>(value + n.value);
    }
    Unit<d-d2,t-t2> operator/(Unit<d2,t2> n)
    {
        return Unit<d-d2,t-t2>(value + n.value);
    }
    etc....
};

#define Distance Unit<1,0>
#define Time     Unit<0,1>
#define Second   Time(1.0)
#define Meter    Distance(1.0)

void foo()
{
   Distance moved1 = 5 * Meter;
   Distance moved2 = 10 * Meter;
   Time time1 = 10 * Second;
   Time time2 = 20 * Second;
   if ((moved1 / time1) == (moved2 / time2))
       printf("Same speed!");
}

Шаблон позволяет компилятору динамически создавать и использовать безопасные для типов экземпляры шаблона на лету. Во время компиляции компилятор фактически выполняет математику параметра-шаблона, создавая отдельные классы, где это необходимо для каждого уникального результата. Существует неявный тип Unit <1, -1> (расстояние / время = скорость), который создается и сравнивается в условном выражении, но никогда явно не объявляется в коде.

Очевидно, кто-то в университете определил шаблон такого типа с 40+ параметрами (нужен справочник), каждый из которых представляет отдельный тип физических единиц. Подумайте о безопасности типов такого класса, только для своих чисел.

29 голосов
/ 08 октября 2008

Они анализируются компилятором , а не препроцессором, который запускает до компилятора.

Вот что MSDN говорит об этом: http://msdn.microsoft.com/en-us/library/aa903548(VS.71).aspx

Вот некоторые проблемы с макросом:

  • Компилятор не может проверить, что параметры макроса имеют совместимые типы.
  • Макрос раскрывается без какой-либо специальной проверки типов.
  • Параметры i и j оцениваются дважды. Например, если какой-либо параметр имеет постинкрементную переменную, приращение выполняется два раза.
  • Поскольку макросы раскрываются препроцессором, сообщения об ошибках компилятора будут ссылаться на расширенный макрос, а не на само определение макроса. Кроме того, макрос будет отображаться в развернутом виде во время отладки.

Если вам этого недостаточно, я не знаю, что есть.

22 голосов
/ 14 декабря 2009

Ответ такой длинный, что я не могу подвести итог, но:

  • например, макросы не гарантируют безопасность типов, в то время как шаблоны функций: у компилятора нет возможности проверить, что параметры макросов имеют совместимые типы - также во время создания шаблона функции компилятор знает, int или float определить operator +
  • шаблоны открывают дверь для метапрограммирования (короче говоря, оценивая вещи и принимая решение во время компиляции): во время компиляции можно узнать, является ли тип целым или с плавающей точкой; будь то указатель или квалифицированный констант и т. д. см. «черты типа» в следующем c ++ 0x
  • шаблоны классов имеют частичную специализацию
  • шаблоны функций имеют явную полную специализацию, в вашем примере add<float>(5, 3); может быть реализован иначе, чем add<int>(5, 3);, что невозможно с макросами
  • макрос не имеет области действия
  • #define min(i, j) (((i) < (j)) ? (i) : (j)) - параметры i и j вычисляются дважды. Например, если какой-либо параметр имеет постинкрементную переменную, приращение выполняется два раза
  • , поскольку макросы раскрываются препроцессором, сообщения об ошибках компилятора будут ссылаться на расширенный макрос, а не на само определение макроса. Кроме того, макрос будет отображаться в развернутом виде во время отладки
  • и т.д ...

Примечание: В некоторых редких случаях я предпочитал полагаться на вариационные макросы, потому что нет такого понятия, как вариационные шаблоны, пока c ++ 0x не станет основным. C ++ 11 - это жить.

Ссылки

12 голосов
/ 14 декабря 2009

На самом базовом уровне, да, шаблоны - это просто замены макросов. Но вы пропускаете множество вещей, думая об этом таким образом.

Рассмотрим специализацию шаблонов, которую, насколько мне известно, вы не можете смоделировать с помощью макросов. Это не только обеспечивает специальную реализацию для определенных типов, но и является одной из ключевых частей шаблонного метапрограммирования:

template <typename T>
struct is_void
{
    static const bool value = false;
}

template <>
struct is_void<void>
{
    static const bool value = true;
}

Что само по себе является лишь одним из примеров многих вещей, которые вы можете сделать . Сами шаблоны завершены по Тьюрингу.

Это игнорирует самые базовые вещи, такие как область видимости, безопасность типов и макросы, которые являются более сложными.

10 голосов
/ 14 декабря 2009

NO . Один простой контрпример: шаблоны соответствуют пространствам имен, макросы игнорируют пространства имен (так как они являются инструкциями препроцессора).

namespace foo {
    template <class NumberType>
    NumberType add(NumberType a, NumberType b)
    {
        return a+b;
    }

    #define ADD(x, y) ((x)+(y))
} // namespace foo

namespace logspace 
{
    // no problemo
    template <class NumberType>
    NumberType add(NumberType a, NumberType b)
    {
        return log(a)+log(b);
    }

    // redefintion: warning/error/bugs!
    #define ADD(x, y) (log(x)+log(y))

} // namespace logspace
9 голосов
/ 08 октября 2008

Шаблоны C ++ напоминают макросы Lisp (не макросы C) в том, что они работают с уже проанализированной версией кода и позволяют генерировать произвольный код во время компиляции. К сожалению, вы программируете что-то похожее на необработанное лямбда-исчисление, поэтому такие сложные методы, как циклы, довольно громоздки. Все подробности можно найти в Генеративном программировании , написанном Krysztof Czarnecki и Ulrich Eisenecker.

6 голосов
/ 08 октября 2008

В случае, если вы ищете более глубокое рассмотрение предмета, я могу обратиться к каждому из вас любимый ненавистник C ++ Этот человек знает и ненавидит больше C ++, чем я когда-либо мог мечтать. Это одновременно делает FQA невероятно подстрекательским и отличным ресурсом.

5 голосов
/ 14 декабря 2009
  • шаблоны безопасны.
  • Шаблонные объекты / типы могут быть пространством имен, сделаны частными членами класса и т. Д.
  • параметры для шаблонных функций не копируются по всему телу функции.

Это действительно большое дело и предотвращает множество ошибок.

4 голосов
/ 14 декабря 2009

Этот ответ предназначен для того, чтобы пролить свет на препроцессор C и на то, как его можно использовать для общего программирования


Они в некотором отношении, поскольку они допускают некоторую похожую семантику. Препроцессор C был использован для включения общих структур данных и алгоритмов (см. token Concatination ). Однако, без учета каких-либо других возможностей шаблонов C ++, он делает всю общую игру программирования LOT CLEARER удобной для чтения и реализации.

Если кто-то хочет увидеть только хардкорное C-программирование в действии, прочтите исходный код libevent - здесь также упоминается здесь Реализована обширная коллекция контейнеров / алгоритмов, и это сделано в заголовочном файле SINGLE (очень читабельный). Я действительно восхищаюсь этим, код шаблона C ++ (который я предпочитаю для его других атрибутов) ОЧЕНЬ подробен.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...