Новые кодеры иногда пишут такой код:
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
для мешка с именами со связанными значениями.
Шаблоны похожи только на макросы в их основной функции. В конце концов, шаблоны были введены в язык как «цивилизованная» альтернатива макросам. Но даже когда дело доходит до того, что большинство базовых функций, схожесть только глубокая.
Однако, как только мы перейдем к более продвинутым функциям шаблонов, например специализация (частичная или явная), любое очевидное сходство с макросами полностью исчезает.
Шаблоны могут делать намного больше, чем может сделать препроцессор макроса.
Например. существуют шаблонные специализации: Если этот шаблон инстанцируется с этим типом или константой, чем не использовать реализацию по умолчанию, но этот здесь ...
... шаблоны могут что некоторые параметры одного типа и т. д. ...
Вот некоторые источники. Вы можете посмотреть:
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
Шаблоны понимают типы данных. Макросы этого не делают.
Это означает, что вы можете делать такие вещи, как следующие ...
Кроме того, поскольку шаблоны безопасны по типу, представляют собой несколько методов кодирования шаблонов, которые могли бы быть выполнены с помощью некоторого гипотетического расширенного препроцессора, но в лучшем случае были бы клочковыми и подверженными ошибкам (например, параметры шаблона шаблона , аргументы шаблона по умолчанию, шаблоны политик, как обсуждалось в Современный дизайн C ++).
Это не ответ, как следствие уже высказанных ответов.
Работа с учеными, хирургами, художниками-графиками и другими, которые должны программировать, но не являются и никогда не станут профессиональными разработчиками программного обеспечения полного времени - я вижу, что макросы легко понятны случайным программистом, в то время как шаблоны, по-видимому, требуют более высокого уровня абстрактного мышления, возможно только при более глубоком и постоянном программировании опыта на C ++. Требуется много примеров работы с кодом, где шаблоны являются полезной концепцией, поскольку концепция имеет смысл достаточно для использования. Хотя это можно сказать о какой-либо языковой функции, объем опыта для шаблонов представляет собой больший разрыв, чем специалист-случайный программист, скорее всего, получит от своей повседневной работы.
Средний астроном или инженер-электроник, вероятно, сталкиваются с макросами просто прекрасно, может даже понять, почему макросов следует избегать, но не будет достаточно шаблонов для повседневного использования. В этом контексте макросы на самом деле лучше. Естественно, существует множество карманов исключений; некоторые физики проводят круги вокруг разработчиков программного обеспечения, но это не типично.
Есть некоторые основные проблемы с макросами.
Во-первых, они не учитывают область или тип. Если у меня есть #define max(a, b)...
, то всякий раз, когда у меня есть токен max
в моей программе, по какой-то причине он будет заменен. Он будет заменен, если это имя переменной или глубоко внутри вложенных областей. Это может вызвать затруднительные ошибки компиляции. Напротив, шаблоны работают внутри системы типа C ++. Функция шаблона может использовать свое имя повторно в пределах области видимости и не будет пытаться переписать имя переменной.
Во-вторых, макросы не могут быть изменены. Шаблон std::swap
обычно просто объявляет временную переменную и выполняет очевидные назначения, потому что это очевидный способ, который обычно работает. Это ограничение будет ограничено макросом. Это было бы крайне неэффективно для больших векторов, поэтому векторы имеют специальный swap
, который меняет местами ссылки, а не весь контент. (Это оказывается очень важным в материалах, который средний программист на C ++ не должен писать, но он использует.)
В-третьих, макросы не могут делать какие-либо формы ввода типов. Во-первых, вы не можете написать общий своп-макрос, поскольку он должен будет объявить переменную типа, и он не знает, какой тип может быть.
Одним из ярких примеров мощностей шаблонов является то, что изначально называлось стандартной библиотекой шаблонов, которая находится в стандарте как контейнеры, алгоритмы и итераторы. Посмотрите, как они работают, и попытайтесь подумать, как вы замените его макросами. Александр Степанов просмотрел большое количество языков для реализации своих идей STL и пришел к выводу, что C ++ с шаблонами был единственным, над которым он работал.
Макросы - это механизм подстановки текста.
Шаблоны - это функциональный язык, который выполняется во время компиляции, и интегрирован в систему типа C ++. Вы можете думать о них как о плагинов для языка.
C ++ шаблоны вроде как макросы Lisp (а не макросы C), поскольку они работают с уже проанализированной версией кода, и они позволяют генерировать произвольный код во время компиляции. К сожалению, вы программируете что-то похожее на исходное исчисление Lambda, поэтому передовые методы, такие как цикл, являются громоздкими. Во всех деталях gory см. Generative Programming : Krysztof Czarnecki и Ulrich Eisenecker.
На самом базовом уровне да, шаблоны - это просто замены макросов. Но вы пропустите много вещей, подумав об этом таким образом.
Рассмотрите специализированную специализацию, которая, насколько мне известно, вы не можете имитировать с помощью макросов. Это не только позволяет специальную реализацию для определенных типов, но и является одной из ключевых частей метапрограммирования шаблонов:
template <typename T>
struct is_void
{
static const bool value = false;
}
template <>
struct is_void<void>
{
static const bool value = true;
}
. Это само по себе является лишь одним из примеров многих вещей вы можете сделать . Шаблоны сами по себе являются Тьюрингом.
Это игнорирует самые основные вещи, такие как область действия, тип безопасности, и что макросы более беспорядочны.
Ответ так длинный, что я не могу подвести итог всему, но:
int
или float
шаблоны operator +
add<float>(5, 3);
может быть реализовано иначе, чем add<int>(5, 3);
, что невозможно с макросами #define min(i, j) (((i) < (j)) ? (i) : (j))
- параметры i
и j
оцениваются дважды. Например, если какой-либо параметр имеет постинкрементную переменную, инкремент выполняется два раза Примечание. В некоторых редких случаях я предпочел полагаться на переменные макросы, потому что есть нет такой вещи, как вариативные шаблоны, пока c ++ 0x не станет основным. C ++ 11 является живым.
Ссылки:
Ключевое слово typename представлено, чтобы включить вложенные в контекст вложенные typdef. Они были необходимы для метода признаков, которые позволяют добавлять метаданные к типам (особенно встроенным типам, таким как указатель), это было необходимо для записи STL. Ключевое слово typename в остальном совпадает с ключевым словом class.
Шаблоны интегрированы в язык и безопасны по типу.
Расскажите, как вы это сделаете с помощью макросов. Это тяжелое метапрограммирование шаблонов.
https://www.youtube.com/watch?v=0A9pYr8wevk
Я думаю, что макросы AFAIK не могут вычислить типы так, как это могут сделать частичные специализации шаблона.
Здесь много комментариев, пытающихся различать макросы и шаблоны.
Да - они оба одинаковы: инструменты генерации кода.
Макросы являются примитивной формой, без много принуждения компилятора (например, делать объекты в 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 + параметрами (нужна ссылка), каждая из которых представляет собой отдельный тип физической единицы. Подумайте о типе безопасности такого класса, просто для ваших номеров.
Нет, это невозможно. Препроцессор (едва) достаточен для нескольких вещей, таких как контейнеры 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
сообщает компилятору, что конкретное имя предназначено для обращения к типу, а не переменной / значению, поэтому (например) вы можете определить другие переменные этого типа.
Что-то, о чем не упоминалось, состоит в том, что функции шаблонов могут выводить типы параметров.
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.
Попробуем примитивный пример. Рассмотрим
#define min(a,b) ((a)<(b))?(a):(b)
, вызывается как
c = min(a++,++b);
Конечно, реальная разница глубже, но этого должно быть достаточно, чтобы отбросить сходство с макросами.
Изменить: И нет, вы не можете обеспечить безопасность типов с помощью макросов. Как бы вы реализовали typesafe min()
для каждого типа, определяющего меньше сравнения (т. Е. operrator<
)?
min(a, b)
, который не будет дважды оценивать аргумент, и вы не только установите свою точку зрения, но и произведете на меня большое впечатление. Я не думаю, что это можно сделать.
– David Thornley
14 December 2009 в 22:25
Шаблоны безопасны по типу. С помощью define вы можете иметь код, который компилируется, но все же не работает правильно.
Макросы расширяются до того, как компилятор попадает в код. Это означает, что вы получите сообщение об ошибке для расширенного кода, а отладчик видит только расширенную версию.
С помощью макросов всегда есть шанс, что некоторое выражение будет оцениваться дважды. Представьте, что в качестве параметра передается что-то вроде ++ x.
Это действительно большое дело и предотвращает множество ошибок.
На мой взгляд, макросы - это плохая привычка от 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 на шаблоны ! Вы можете использовать шаблоны для выполнения так называемых черт, которые используются, имеет своего рода документацию для типов и т. Д. Вы можете сделать гораздо больше с шаблонами, чем возможно с помощью макросов!
Хотя параметры шаблона проверяются по типу и есть много преимуществ шаблонов над макросами, шаблоны очень похожи на макросы, поскольку они все еще основаны на замене текста. Компилятор не будет проверять, что ваш код шаблона имеет смысл, пока вы не дадите ему параметры типа для замены. Например, Visual C ++ не жалуется на эту функцию, если вы на самом деле ее не называете:
template<class T>
void Garbage(int a, int b)
{
fdsa uiofew & (a9 s) fdsahj += *! wtf;
}
Следовательно, в общем случае невозможно узнать, будет ли работать ваш шаблон кода правильно или скомпилировать для определенной категории параметров типа, которые шаблон предназначен для принятия.
Они разбираются компилятором , а не препроцессором, который запускает перед компилятором.
Вот что говорит MSDN об этом: http://msdn.microsoft.com/en-us/library/aa903548 (VS.71) .aspx
Вот некоторые проблемы с макросом:
blockquote>
- Компилятор не может проверить, что макропараметры совместимы.
- Макрос расширяется без специальной проверки типа.
- Параметры i и j оцениваются дважды. Например, если какой-либо параметр имеет постинкрементную переменную, инкремент выполняется два раза.
- Поскольку макросы расширяются препроцессором, сообщения об ошибках компилятора будут ссылаться на расширенный макрос, а не на определение макроса. Кроме того, макрос будет отображаться в расширенной форме во время отладки.
Если этого недостаточно для вас, я не знаю, что есть.
Если вы ищете более углубленное изучение предмета, я могу превратить вас в любимого любителя C ++ каждого . Этот человек знает и ненавидит больше C ++, чем я могу когда-либо мечтать. Это одновременно делает FQA невероятно воспалительным и отличным ресурсом.
Шаблоны предлагают некоторую степень безопасности типов.
Шаблоны могут быть помещены в пространства имен или быть членами класса. Макросы - это только шаг предварительной обработки. В принципе, шаблоны являются членами первого класса языка, который играет красиво (лучше?) Со всем остальным.
Этот ответ предназначен для прояснения препроцессора C и того, как он может использоваться для общего программирования
. Они в некоторых отношениях, поскольку они позволяют использовать некоторую подобную семантику. Препроцессор C использовался для создания общих структур данных и алгоритмов (см. токен Concatination ). Однако, не учитывая никаких других особенностей шаблонов C ++, он делает всю родовую программирующую игру LOT CLEARER для чтения и реализации.
Если кто-то хочет видеть хардкор C, то только общее программирование в действии читает libevent sourcecode - здесь также упоминается здесь . Реализована обширная коллекция контейнеров / алгоритмов, и ее сделано в файле заголовка SINGLE (очень читаемо). Я действительно восхищаюсь этим, код шаблона C ++ (который я предпочитаю для других его атрибутов) ОЧЕНЬ многословный.