Как шаблоны отличаются от макросов на C ++? [Дубликат]

Новые кодеры иногда пишут такой код:

my_calculator.button_0 = tkinter.Button(root, text=0)
my_calculator.button_1 = tkinter.Button(root, text=1)
my_calculator.button_2 = tkinter.Button(root, text=2)
...

Затем кодер остается с кучей именованных переменных с усилием кодирования O ( m * n ), где m - это число именованных переменных, а n - это количество раз, к которому необходимо получить доступ к группе переменных (включая создание) , Более проницательный новичок отмечает, что единственная разница в каждой из этих строк - это число, которое изменяется на основе правила и решает использовать цикл. Тем не менее, они зациклились на том, как динамически создавать эти имена переменных, и могут попробовать что-то вроде этого:

for i in range(10):
    my_calculator.('button_%d' % i) = tkinter.Button(root, text=i)

Вскоре они обнаруживают, что это не сработает.

Если программа требует произвольных переменных «имена», лучше всего подходит словарь, как объясняется в других ответах. Однако, если вы просто пытаетесь создать много переменных, и вы не возражаете ссылаться на них с последовательностью целых чисел, вы, вероятно, ищете list. Это особенно верно, если ваши данные однородны, например, ежедневные показания температуры, еженедельные оценки викторины или сетка графических виджета.

Это можно собрать следующим образом:

my_calculator.buttons = []
for i in range(10):
    my_calculator.buttons.append(tkinter.Button(root, text=i))

Этот list также может быть создан в одной строке с пониманием:

my_calculator.buttons = [tkinter.Button(root, text=i) for i in range(10)]

Результат в любом случае - это заполненный list, с первым элементом, к которому обращаются с помощью my_calculator.buttons[0], следующего с my_calculator.buttons[1] и т. д. Имя переменной «base» становится именем list, и для доступа к нему используется различный идентификатор.

Наконец, не забудьте другие структуры данных, такие как set - это аналогично словарю, за исключением того, что каждое «имя» не имеет привязанного к нему значения. Если вам просто нужна «сумка» объектов, это может быть отличным выбором. Вместо этого:

keyword_1 = 'apple'
keyword_2 = 'banana'

if query == keyword_1 or query == keyword_2:
    print('Match.')

У вас будет следующее:

keywords = {'apple', 'banana'}
if query in keywords:
    print('Match.')

Используйте последовательность list для последовательности похожих объектов, a set для произвольного - помещенный пакет предметов или dict для мешка с именами со связанными значениями.

47
задан Roddy 7 October 2008 в 21:43
поделиться

25 ответов

Шаблоны похожи только на макросы в их основной функции. В конце концов, шаблоны были введены в язык как «цивилизованная» альтернатива макросам. Но даже когда дело доходит до того, что большинство базовых функций, схожесть только глубокая.

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

2
ответ дан AnT 22 August 2018 в 20:33
поделиться

Шаблоны могут делать намного больше, чем может сделать препроцессор макроса.

Например. существуют шаблонные специализации: Если этот шаблон инстанцируется с этим типом или константой, чем не использовать реализацию по умолчанию, но этот здесь ...

... шаблоны могут что некоторые параметры одного типа и т. д. ...


Вот некоторые источники. Вы можете посмотреть:

  • C ++ шаблоны Вандерворд и Джоссютис. Это самая лучшая и самая полная книга о шаблонах, которые я знаю.
  • Библиотека boost состоит почти полностью из определений шаблонов.
2
ответ дан Black 22 August 2018 в 20:33
поделиться
[Д0] НЕТ. Один простой пример счетчика: шаблоны соответствуют пространствам имен, макросы игнорируют пространства имен (поскольку они являются инструкциями препроцессора).

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
10
ответ дан catchmeifyoutry 22 August 2018 в 20:33
поделиться

Шаблоны понимают типы данных. Макросы этого не делают.

Это означает, что вы можете делать такие вещи, как следующие ...

  • Определить операцию (например, одну для номеров обертывания ), которые могут принимать любой тип данных, а затем предоставляют специализации, которые выбирают соответствующий алгоритм, основанный на том, является ли тип данных интегральным или с плавающей точкой
  • . Определите аспекты ваших типов данных во время компиляции, разрешив трюки типа шаблонный размер массива , который Microsoft использует для перегрузок C ++ strcpy_s и его ilk

Кроме того, поскольку шаблоны безопасны по типу, представляют собой несколько методов кодирования шаблонов, которые могли бы быть выполнены с помощью некоторого гипотетического расширенного препроцессора, но в лучшем случае были бы клочковыми и подверженными ошибкам (например, параметры шаблона шаблона , аргументы шаблона по умолчанию, шаблоны политик, как обсуждалось в Современный дизайн C ++).

4
ответ дан Community 22 August 2018 в 20:33
поделиться

Это не ответ, как следствие уже высказанных ответов.

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

Средний астроном или инженер-электроник, вероятно, сталкиваются с макросами просто прекрасно, может даже понять, почему макросов следует избегать, но не будет достаточно шаблонов для повседневного использования. В этом контексте макросы на самом деле лучше. Естественно, существует множество карманов исключений; некоторые физики проводят круги вокруг разработчиков программного обеспечения, но это не типично.

1
ответ дан DarenW 22 August 2018 в 20:33
поделиться

Есть некоторые основные проблемы с макросами.

Во-первых, они не учитывают область или тип. Если у меня есть #define max(a, b)..., то всякий раз, когда у меня есть токен max в моей программе, по какой-то причине он будет заменен. Он будет заменен, если это имя переменной или глубоко внутри вложенных областей. Это может вызвать затруднительные ошибки компиляции. Напротив, шаблоны работают внутри системы типа C ++. Функция шаблона может использовать свое имя повторно в пределах области видимости и не будет пытаться переписать имя переменной.

Во-вторых, макросы не могут быть изменены. Шаблон std::swap обычно просто объявляет временную переменную и выполняет очевидные назначения, потому что это очевидный способ, который обычно работает. Это ограничение будет ограничено макросом. Это было бы крайне неэффективно для больших векторов, поэтому векторы имеют специальный swap, который меняет местами ссылки, а не весь контент. (Это оказывается очень важным в материалах, который средний программист на C ++ не должен писать, но он использует.)

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

Одним из ярких примеров мощностей шаблонов является то, что изначально называлось стандартной библиотекой шаблонов, которая находится в стандарте как контейнеры, алгоритмы и итераторы. Посмотрите, как они работают, и попытайтесь подумать, как вы замените его макросами. Александр Степанов просмотрел большое количество языков для реализации своих идей STL и пришел к выводу, что C ++ с шаблонами был единственным, над которым он работал.

1
ответ дан David Thornley 22 August 2018 в 20:33
поделиться

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

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

50
ответ дан Ferruccio 22 August 2018 в 20:33
поделиться

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

9
ответ дан Glomek 22 August 2018 в 20:33
поделиться

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

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

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

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

. Это само по себе является лишь одним из примеров многих вещей вы можете сделать . Шаблоны сами по себе являются Тьюрингом.

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

12
ответ дан GManNickG 22 August 2018 в 20:33
поделиться

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

  • , например, макросы не обеспечивают безопасность типа, а функциональные шаблоны: нет возможности для проверки компилятором что макропараметры являются совместимыми типами - также в момент создания шаблона функции компилятор знает, определяют ли 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 является живым.

Ссылки:

21
ответ дан Gregory Pakosz 22 August 2018 в 20:33
поделиться
  • 1
    У нас есть сердитый downvoter здесь ;-) Я также не знаю, за что я был забит ;-) – Michael Krelin - hacker 14 December 2009 в 19:29
  • 2
    Вы, возможно, сбиты другим респондентом, который хочет, чтобы его / ее ответ сначала попал в верхнюю часть списка, где он скорее будет отмечен как принятый ответ. – Shaggy Frog 14 December 2009 в 19:30
  • 3
    потому что он считает, что его неспособность задавать вопрос является достаточной причиной обвинить его в том, кто не дал ответ, который ему нужен. Я почти соблазняюсь понизить вопрос, но мне нужен способ сжать человека ;-) – Michael Krelin - hacker 14 December 2009 в 19:38
  • 4
    Теперь, когда вопросы были объединены, я чувствую себя глупо - мои комментарии, похоже, обвиняют плакат в том, что он никогда не делал ;-) – Michael Krelin - hacker 14 December 2009 в 22:34
  • 5
    Может кто-нибудь объяснить, почему мой летний вопрос внезапно грохочет клетками всех? Я думаю, что я должен потерять этот сюжет здесь ... – Roddy 14 December 2009 в 22:40

Ключевое слово typename представлено, чтобы включить вложенные в контекст вложенные typdef. Они были необходимы для метода признаков, которые позволяют добавлять метаданные к типам (особенно встроенным типам, таким как указатель), это было необходимо для записи STL. Ключевое слово typename в остальном совпадает с ключевым словом class.

2
ответ дан Hassan Syed 22 August 2018 в 20:33
поделиться

Шаблоны интегрированы в язык и безопасны по типу.

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

https://www.youtube.com/watch?v=0A9pYr8wevk

Я думаю, что макросы AFAIK не могут вычислить типы так, как это могут сделать частичные специализации шаблона.

0
ответ дан ioquatix 22 August 2018 в 20:33
поделиться

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

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

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

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

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

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

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 & lt; 1, -1> (расстояние / время = скорость), который создается и сравнивается в условном выражении, но никогда явно не объявляется в коде.

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

27
ответ дан Jeff B 22 August 2018 в 20:33
поделиться
  • 1
    У меня было некоторое представление о том, что вы пытались сделать, пока я не добрался до «Unit & lt; d + d2, t + t2 & gt;», когда я потерял график. Можете ли вы объяснить, что он пытается сделать и какие преимущества он дает? «Typedef double Distance» / «typedef double Time», который, похоже, даст тот же результат? – Roddy 7 October 2008 в 23:40
  • 2
    Объявите две переменные: Расстояние d; Время t; Если расстояние и время удваиваются, утверждение (d = t) и выражение (d == t) являются действительными. Шаблон предотвращает это - обеспечение безопасности типа для числовых значений. – Jeff B 8 October 2008 в 16:23
  • 3
    Ах! Благодарю. Я бы никогда не смог это сделать для себя! – Roddy 8 October 2008 в 19:49
  • 4
    @@ Родди: +1 для 'Deduce that' – Chubsdad 16 September 2010 в 04:27
  • 5
    «Макросы могут быть очень полезными для встраивания статических таблиц структурированных данных в ваш код». - это не одна из тех, которые не могут сделать шаблоны. ;) Есть вещи, которые шаблоны не могут сделать, но в основном это овеществление; создание новых токенов и т. д. – Yakk - Adam Nevraumont 27 February 2017 в 19:40

Нет, это невозможно. Препроцессор (едва) достаточен для нескольких вещей, таких как контейнеры T, но это просто недостаточно для многих других вещей, которые могут делать шаблоны.

Для некоторых реальных примеров прочитайте Modern C ++ Programming , Andre Alexandrescu, или C ++ метапрограммирования Дейвом Абрахамом и Алексеем Гуртовым. Почти ничего не делается в любой книге, можно моделировать до предельно минимальной степени с препроцессором.

Изменить: что касается typename, это требование довольно просто. Компилятор не всегда может определить, относится ли зависимое имя к типу или нет. Использование typename явно сообщает компилятору, что оно относится к типу.

struct X { 
    int x;
};

struct Y {
    typedef long x;
};

template <class T>
class Z { 
    T::x;
};

Z<X>; // T::x == the int variable named x
Z<Y>; // T::x == a typedef for the type 'long'

typename сообщает компилятору, что конкретное имя предназначено для обращения к типу, а не переменной / значению, поэтому (например) вы можете определить другие переменные этого типа.

4
ответ дан Jerry Coffin 22 August 2018 в 20:33
поделиться

Что-то, о чем не упоминалось, состоит в том, что функции шаблонов могут выводить типы параметров.

template <typename T>
void func(T t)
{
  T make_another = t;

Можно утверждать, что предстоящий оператор «typeof» может исправить это, но даже он не может разделиться другие шаблоны:

template <typename T>
void func(container<T> c)

или даже:

template <tempate <typename> class Container, typename T>
void func(Container<T> ct)

Я также считаю, что предмет специализации недостаточно охвачен. Вот простой пример того, что макросы не могут сделать:

template <typename T>
T min(T a, T B)
{
  return a < b ? a : b;
}

template <>
char* min(char* a, char* b)
{
  if (strcmp(a, b) < 0)
    return a;
  else
    return b;
}

Пространство слишком мало, чтобы перейти к специализации по типу, но то, что вы можете с ним сделать, насколько мне известно, - это ум -blowing.

4
ответ дан keraba 22 August 2018 в 20:33
поделиться

Попробуем примитивный пример. Рассмотрим

#define min(a,b) ((a)<(b))?(a):(b)

, вызывается как

c = min(a++,++b);

Конечно, реальная разница глубже, но этого должно быть достаточно, чтобы отбросить сходство с макросами.

Изменить: И нет, вы не можете обеспечить безопасность типов с помощью макросов. Как бы вы реализовали typesafe min() для каждого типа, определяющего меньше сравнения (т. Е. operrator<)?

2
ответ дан Michael Krelin - hacker 22 August 2018 в 20:33
поделиться
  • 1
    Прочтите мои ответы на ответы, это не тот ответ, который я ищу. – static_rtti 14 December 2009 в 19:30
  • 2
    в то время как ваш макрос, как написано, делает что-то неожиданное, он может быть написан так, чтобы оценивать аргументы только один раз. (не уменьшилось) – just somebody 14 December 2009 в 19:34
  • 3
    Err ... Это почему вы downvote? – Michael Krelin - hacker 14 December 2009 в 19:35
  • 4
    только один, я сомневаюсь, что это возможно. И даже если это возможно, вы можете придумать лучший пример, используя ту же идею. – Michael Krelin - hacker 14 December 2009 в 19:36
  • 5
    @ just someone: Хорошо, напишите макрос min(a, b), который не будет дважды оценивать аргумент, и вы не только установите свою точку зрения, но и произведете на меня большое впечатление. Я не думаю, что это можно сделать. – David Thornley 14 December 2009 в 22:25

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

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

С помощью макросов всегда есть шанс, что некоторое выражение будет оцениваться дважды. Представьте, что в качестве параметра передается что-то вроде ++ x.

2
ответ дан Milan Babuškov 22 August 2018 в 20:33
поделиться
  • шаблоны являются типичными.
  • templated objects / types могут быть помещены в имена, сделанные частными членами класса и т. д.
  • параметры для шаблонных функций не реплицируются по всей функции тело.

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

5
ответ дан moonshadow 22 August 2018 в 20:33
поделиться

На мой взгляд, макросы - это плохая привычка от C. Хотя они могут быть полезны для некоторых, я не вижу реальной потребности в них, когда есть typedefs и шаблоны. Шаблоны являются естественным продолжением объектно-ориентированного программирования. Вы можете сделать намного больше с шаблонами ...

Рассмотрим это ...

int main()
{
    SimpleList<short> lstA;
    //...
    SimpleList<int> lstB = lstA; //would normally give an error after trying to compile
}

Чтобы сделать преобразование, вы можете использовать то, что называется конструктором преобразования и конструктор последовательности (посмотрите на конец) по довольно полному примеру для списка:

#include <algorithm>

template<class T>
class SimpleList
{
public:
    typedef T value_type;
    typedef std::size_t size_type;

private:
    struct Knot
    {
        value_type val_;
        Knot * next_;
        Knot(const value_type &val)
        :val_(val), next_(0)
        {}
    };
    Knot * head_;
    size_type nelems_;

public:
    //Default constructor
    SimpleList() throw()
    :head_(0), nelems_(0)
    {}
    bool empty() const throw()
    { return size() == 0; }
    size_type size() const throw()
    { return nelems_; }

private:
    Knot * last() throw() //could be done better
    {
        if(empty()) return 0;
        Knot *p = head_;
        while (p->next_)
            p = p->next_;
        return p;
    }

public:
    void push_back(const value_type & val)
    {
        Knot *p = last();
        if(!p)
            head_ = new Knot(val);
        else
            p->next_ = new Knot(val);
        ++nelems_;
    }
    void clear() throw()
    {
        while(head_)
        {
            Knot *p = head_->next_;
            delete head_;
            head_ = p;
        }
        nelems_ = 0;
    }
    //Destructor:
    ~SimpleList() throw()
    { clear(); }
    //Iterators:
    class iterator
    {
        Knot * cur_;
    public:
        iterator(Knot *p) throw()
        :cur_(p)
        {}
        bool operator==(const iterator & iter)const throw()
        { return cur_ == iter.cur_; }
        bool operator!=(const iterator & iter)const throw()
        { return !(*this == iter); }
        iterator & operator++()
        {
            cur_ = cur_->next_;
            return *this;
        }
        iterator operator++(int)
        {
            iterator temp(*this);
            operator++();
            return temp;
        }
        value_type & operator*()throw()
        { return cur_->val_; }
        value_type operator*() const
        { return cur_->val_; }
        value_type operator->()
        { return cur_->val_; }
        const value_type operator->() const
        { return cur_->val_; }
    };
    iterator begin() throw()
    { return iterator(head_); }
    iterator begin() const throw()
    { return iterator(head_); }
    iterator end() throw()
    { return iterator(0); }
    iterator end() const throw()
    { return iterator(0); }
    //Copy constructor:
    SimpleList(const SimpleList & lst)
    :head_(0), nelems_(0)
    {
        for(iterator i = lst.begin(); i != lst.end(); ++i)
            push_back(*i);
    }
    void swap(SimpleList & lst) throw()
    {
        std::swap(head_, lst.head_);
        std::swap(nelems_, lst.nelems_);
    }
    SimpleList & operator=(const SimpleList & lst)
    {
        SimpleList(lst).swap(*this);
        return *this;
    }
    //Conversion constructor
    template<class U>
    SimpleList(const SimpleList<U> &lst)
    :head_(0), nelems_(0)
    {
        for(typename SimpleList<U>::iterator iter = lst.begin(); iter != lst.end(); ++iter)
            push_back(*iter);
    }
    template<class U>
    SimpleList & operator=(const SimpleList<U> &lst)
    {
        SimpleList(lst).swap(*this);
        return *this;
    }
    //Sequence constructor:
    template<class Iter>
    SimpleList(Iter first, Iter last)
    :head_(0), nelems_(0)
    {
        for(;first!=last; ++first)
            push_back(*first);


    }
};

Посмотрите на информацию cplusplus.com на шаблоны ! Вы можете использовать шаблоны для выполнения так называемых черт, которые используются, имеет своего рода документацию для типов и т. Д. Вы можете сделать гораздо больше с шаблонами, чем возможно с помощью макросов!

2
ответ дан Partial 22 August 2018 в 20:33
поделиться

Хотя параметры шаблона проверяются по типу и есть много преимуществ шаблонов над макросами, шаблоны очень похожи на макросы, поскольку они все еще основаны на замене текста. Компилятор не будет проверять, что ваш код шаблона имеет смысл, пока вы не дадите ему параметры типа для замены. Например, Visual C ++ не жалуется на эту функцию, если вы на самом деле ее не называете:

template<class T>
void Garbage(int a, int b)
{
    fdsa uiofew & (a9 s) fdsahj += *! wtf;
}

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

2
ответ дан Qwertie 22 August 2018 в 20:33
поделиться
  • 1
    Он только компилирует эту функцию, если ей это нужно. Вот почему это не дает никакой ошибки. – Partial 13 September 2009 в 23:17

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

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

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

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

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

29
ответ дан rlerallut 22 August 2018 в 20:33
поделиться
  • 1
    Ссылка MSDN использует шаблон для Min, который является в значительной степени конечным «плохим примером». См. Статью Скотта Мейера о шаблонах для Min / Max. aristeia.com/Papers/C++ReportColumns/jan95.pdf – Roddy 7 October 2008 в 23:33
  • 2
    Очевидно, что вы технически правы, но говоря, что один обрабатывается препроцессором, а другой - компилятором, не дает причины, почему один лучше другого. – Roel 8 October 2008 в 09:41
  • 3
    @Roddy Ты несправедлив. Мин как шаблон довольно прост для понимания в его несовершенном состоянии и обеспечивает лучшую защиту, чем макросы. У Alexandrescu есть решение проблемы min / max, но это довольно сложно, слишком сложно для моего вкуса. – rlerallut 8 October 2008 в 22:21
  • 4
    @Roel Ну ... Вот почему я цитирую MSDN. Они довольно явные: проверка типов, защита с двойным шагом, сообщения об ошибках. Они all исходят из того, что он обрабатывается внутри компилятора, вы не можете делать это в препроцессоре. Кто заботится о том, чтобы шаблоны были языком Turing? – rlerallut 8 October 2008 в 22:24
  • 5
    @rlerallut - Да, Мин прост для понимания, но это также НЕ ПОЛЕЗНО, по причинам, установленным SM. По его словам, «Все это говорит о том, что мы говорим о максимальной функции здесь! Как может такая концептуально простая функция вызывать так много неприятностей? & Quot; – Roddy 10 October 2008 в 15:29
  • 1
    За исключением того, что всякий раз, когда я смотрю на FQA, я понимаю, что он действительно не знает, о чем он говорит. Многие из его жалоб связаны с неправильным использованием C ++. – David Thornley 8 September 2009 в 21:50

Шаблоны предлагают некоторую степень безопасности типов.

0
ответ дан tpower 22 August 2018 в 20:33
поделиться
  • 1
    это как сказать «функции предлагают некоторую степень безопасности типов над макросами». в то время как технически верно, это не весь ответ, и, конечно, не предписывающий. – Aaron 9 October 2008 в 04:11

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

2
ответ дан user4891 22 August 2018 в 20:33
поделиться

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


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

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

4
ответ дан Community 22 August 2018 в 20:33
поделиться
Другие вопросы по тегам:

Похожие вопросы: