Ответ 1
Ну, в общем, С++-шаблоны и генераторы С# аналогичны - по сравнению с Java-дженериками, которые совершенно разные, но они также имеют большие различия. Как и в С#, существует поддержка времени выполнения, используя отражение, получая объект, описывающий типы, используемые для создания экземпляров генериков. С++ не имеет отражения, и все, что он делает с типами, выполняется во время компиляции.
Самая большая разница между С# generics и шаблонами С++ - это то, что генераторы С# лучше проверяют тип. Они всегда ограничены в том смысле, что они не позволяют выполнять операции, которые не указаны в момент определения дженериков. Главный конструктор С#, поднятый в качестве причины того, что добавленная сложность потребовала бы иметь подразумеваемые ограничения. Я не очень разбираюсь в С#, поэтому я не могу говорить дальше. Я расскажу о том, как обстоят дела на С++ и как они будут улучшаться, чтобы люди не думали, что материал С++ ошибочен.
В С++ шаблоны не ограничены. Если вы выполняете операцию, то во время определения шаблона подразумевается, что операция будет успешной во время создания экземпляра. Для компилятора С++ даже не требуется, чтобы шаблон синтаксически проверялся на достоверность. Если он содержит синтаксическую ошибку, то эта ошибка должна быть диагностирована при создании экземпляра. Любой диагноз до этого является чистым продуктом реализации.
Те предполагаемые ограничения, которые, как было показано, были легкими для дизайнера шаблонов в краткосрочной перспективе, потому что им не нужно заботиться о том, чтобы указать действительные операции в их интерфейсе шаблона. Они возлагают бремя на пользователя своего шаблона - поэтому пользователь должен убедиться, что он выполняет все эти требования. Часто бывает, что пользователь пытается, по-видимому, выполнять действительные операции, но терпит неудачу, когда компилятор предоставляет пользователю сотни строк сообщений об ошибках в отношении некорректного синтаксиса или не найденных имен. Поскольку компилятор не может знать, какое ограничение, в частности, было нарушено, в первую очередь, оно перечисляет все части путей кода, когда-либо задействованных вокруг неисправного места, и все даже не важные детали, и пользователю придется сканировать сквозное сообщение об ошибке текст.
Это фундаментальная проблема, которую можно решить, просто указав на интерфейс шаблона или обобщения, какие свойства должен иметь параметр типа. С#, насколько я знаю, может ограничивать параметр для реализации интерфейса или наследования базового класса. Он решает это на уровне типа.
Комитет С++ давно видел, что необходимо устранить эти проблемы, и в ближайшее время (возможно, в следующем году), С++ также сможет указать такие явные ограничения. (см. время -машинная заметка ниже), как в следующем случае.
template<typename T> requires VariableType<T>
T f(T a, T b) {
return a + b;
}
Компилятор сигнализирует об ошибке в этой точке, потому что выражение, как написано, не соответствует действительности. Это сначала помогает дизайнеру шаблона написать более правильный код, потому что код уже проверен по типу уже в некоторой степени (ну, что там возможно). Теперь программист может указать это требование:
template<typename T> requires VariableType<T> && HasPlus<T, T>
T f(T a, T b) {
return a + b;
}
Теперь он будет компилятором. Компилятор, увидев, что T
появляется как возвращаемый тип, автоматически подразумевает, что T
является скопируемым, поскольку это использование T
появляется в интерфейсе, а не в корпусе шаблонов. Другие требования были сформулированы с использованием условий требования. Теперь пользователь получит соответствующее сообщение об ошибке, если он использует тип, который не имеет op+
.
С++ 1x отделяет требования от типа. Вышеприведенное работает как для примитивных типов, так и для классов. В этом смысле они более гибкие, но довольно сложные. Правила, которые определяют, когда и когда требования удовлетворяются, длинные... Вы можете с новыми правилами сказать следующее:
template<typename T> requires MyCuteType<T>
void f(T t) { *t = 10; }
А затем вызовите f
с помощью int
! Это будет работать, просто написав концептуальную карту для MyCuteType<int>
, которая учит компилятору, как можно разыменовать int. Это будет очень удобно в таких циклах:
for_each(0, 100, doSomething());
Так как программист может рассказать компилятору, как int может удовлетворить концепцию input iterator
, вы могли бы написать такой код в С++ 1x, если бы вы только написали соответствующую концептуальную карту, которая действительно не все это сложно.
Хорошо, достаточно с этим. Надеюсь, я смогу показать вам, что наличие ограниченных шаблонов не так уж плохо, но на самом деле лучше, потому что отношения между типами betweens и операциями над ними в шаблонах теперь известны компилятору. И я даже не писал о axioms
, что еще одна приятная вещь в концепциях C++1x
. Помните, что это будущий материал, он еще не вышел, но он будет примерно в 2010 году. Тогда нам придется подождать, пока какой-то компилятор выполнит все:)
ОБНОВЛЕНИЕ ОТ "БУДУЩЕГО"
Концепции С++ 0x не были приняты в проект, но были проголосованы в конце 2009 года. Слишком плохо! Но, возможно, мы увидим это снова в следующей версии С++? Пусть все надеются!