LINUX.ORG.RU

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

Поподробнее тут, на англицком, но не сложно - http://en.wikipedia.org/wiki/Comparison_of_Java_and_C%2B%2B#Templates_vs._Gen...

shuthdar ★★★
()

Да, дженерик это аналог void* в плюсах, но только более безопасный.

Booster ★★
()
Ответ на: комментарий от Booster

И это хорошо.

и что плохого, скажем в

public <T extends Container> T get(Class<T> type) { }

При попытке подсунуть левый класс компилятор даст по пальцам, и не надо заморачиваться с проверкой на наследование.

lester_dev ★★★★★
()

Герберт Шилдт «Java 2 v5.0 (Tiger). Новые возможности»

Всё чётко и по-существу.

iZEN ★★★★★
()
Ответ на: комментарий от lester_dev

Проблема в том, что ограничения могут поменяться, но интерфейс менять не есть хорошо. И ограничения на уровне интерфейса это жёстко, только классы или только интегральные типы.
Так или иначе ограничения в плюсах сделать можно, и хорошо, что это не на уровне интерфейса.

Booster ★★
()
Ответ на: комментарий от Booster

Проблема в том, что ограничения могут поменяться, но интерфейс менять не есть хорошо

а чем контракт на шаблонный тип не интерфейс?

jtootf ★★★★★
()
Ответ на: комментарий от jtootf

В том то и дело, что явно декларировать ограничения в самом интерфейсе это не очень удачно. Поменялись у нас ограничения, нужно менять интерфейс, а так «стало»/«не стало» компилиться, но интерфейс остался прежний.

Booster ★★
()
Ответ на: комментарий от Booster

явно декларировать ограничения в самом интерфейсе это не очень удачно

независимо от того, явно или неявно - но ограничения есть. ты считаешь что duck typing лучше явного контракта?

Поменялись у нас ограничения, нужно менять интерфейс

можно пример, в котором ограничения могут поменяться?

jtootf ★★★★★
()
Ответ на: комментарий от jtootf

>независимо от того, явно или неявно - но ограничения есть. ты считаешь что duck typing лучше явного контракта?
Контракт лучше подходит таким языкам как Java. Как понимаю в Java это сделано через интерфейс, в С++ такого явного понятия «интерфейс» нету.

можно пример, в котором ограничения могут поменяться?

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

Booster ★★
()

А что-то типа Boost Spirit на дженериках реализовать можно?

wieker ★★
()
Ответ на: комментарий от iZEN

Герберт Шилдт «Java 2 v5.0 (Tiger). Новые возможности»

Отвратительный автор, после прочтения одного из его руководств по C++ обхожу стороной. Много неточностей, ошибок и не расставленных там где надо акцентов. ACCU большинство его книг не рекомендует к прочтению: http://accu.org/index.php?module=bookreviews&func=search - поиск по автору Herbert Schildt.

Reaper ★★
()
Ответ на: комментарий от Reaper

У Шилдта была пара приличных книг - «Теория и практика С++» и «Самоучитель С++». Всё остальное - редкостное гавно, особенно какие-то «советы/рецепты программирования от шилдта» или как-то так.

LamerOk ★★★★★
()
Ответ на: комментарий от Booster

Контракт лучше подходит таким языкам как Java

как Eiffel, D, Haskell

в С++ такого явного понятия «интерфейс» нету

в С++ нет понятия контракта. концепты хотели ввести в С++0x, но передумали

jtootf ★★★★★
()

общего в них три вещи:

- сделано через жопу

- предваряется <

- заканчивается >


Причем программисты давно молятся чтобы жопу заменили на женскую http://bugs.sun.com/view_bug.do?bug_id=5098163

wfrr ★★☆
()

Женерики это инструмент для параметрического полиморфизма и только.

Шаблоны C++ - и параметрический полиморфизм, и ad-hoc, и вообще тьюринг-полны

Love5an
()
Ответ на: комментарий от jtootf

> ты считаешь что duck typing лучше явного контракта?

это зависит, хотя бы от объема проекта, вот например:

пусть у нас есть 2 класса class User { ... public: Town town; ... }; class Shop { ... public: Town town; ... };

и нам надо

template<class Something> const Town& town_with_maximum_of_something( std::vector<Something> somethings )

реализация думаю понятна

для явного (явного=«как в яве» :-) контракта придется городить интерфейс с геттерами-сеттерами

а теперь вопрос: можно ли этот шаблон использовать хоть как-нибудь неправильно из-за того, что контракт на Something не обозначен явно???

www_linux_org_ru ★★★★★
()
Ответ на: комментарий от www_linux_org_ru

лучше вот так:

template<class Something>
const Town& town_with_maximum_of_something( const std::vector<Something>& somethings )

www_linux_org_ru ★★★★★
()
Ответ на: комментарий от Booster

Навскидку - в с++ нельзя ограничить тип параметров шаблона.

И это хорошо.

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

Miguel ★★★★★
()

В чём разница между generics Явы и templates Ц++ ?

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

Дженерики - не такие. В них код дженерика не разворачивается, и до рантайма не имеет понятия, к чему именно будет применяться; однако, в заголовке дженерика указывается, какой интерфейс ОБЯЗАТЕЛЬНО будет поддерживать то, к чему он будет применён, и компилятор это гарантирует (ну, точнее, старается). Поэтому, в частности, дженерики позволяют писать код, где дженерик применяется к произвольному (не определённому в статике) количеству типов.

Пример (выморочный):

import java.io.*;
interface Foo {
    Integer getNum();
}
class Empty implements Foo {
    public Integer getNum() {
        return 0;
    }
}
class Bar<X extends Foo> implements Foo {
    X Baz;
    Bar(X _Baz){
        Baz = _Baz;
    }
    public Integer getNum() {
        return Baz.getNum() + 1;
    };
};

public class Sample {
    private static <X extends Foo> Integer quux(Integer n, X foo){
        if (n == 0){
            return foo.getNum();
        } else {
            return quux(n-1, new Bar<X>(foo));
        }
    }
    public static void main(String []args){
        try {
            BufferedReader is = new BufferedReader(new InputStreamReader(System.in));
            String line = is.readLine();
            Integer val = Integer.parseInt(line);
            System.out.println(quux(val, new Empty()));
        } catch (NumberFormatException ex) {
            System.err.println("Not a valid number");
        } catch (IOException e) {
            System.err.println("Unexpected IO ERROR");
        }
    }
}
В рантайме может быть создано значение типа Bar<Bar<Bar...<Empty>...>>, причём длина цепочки заранее не определена. Поэтому аналогичный пример на плюсах упадёт (попытается развернуть шаблон бесконечное число раз), а в жабе оно компилится и работает (потому что не пытается разворачивать вообще).

Miguel ★★★★★
()
Ответ на: комментарий от Love5an

Шаблоны C++ - и параметрический полиморфизм, и ad-hoc, и вообще тьюринг-полны

Шаблоны плюсов - НЕ параметрический полиморфизм, а только лишь притворяются. Шаблоны - это такой статический ad-hoc, и более ничего.

Miguel ★★★★★
()
Ответ на: комментарий от Miguel

Лучше километровые матюки при неправильном использовании, чем бессмысленный запрет многих правильных использований.

legolegs ★★★★★
()
Ответ на: комментарий от legolegs

Лучше километровые матюки при неправильном использовании, чем бессмысленный запрет многих правильных использований.

Это каких? Единственное, что требуется при использовании шаблона - поддержка нужного интерфейса. Если у вас этого интерфейса нет, то использование по любому неправильное.

Miguel ★★★★★
()
Ответ на: комментарий от Miguel

s/при использовании шаблона/при использовании дженерика/

Miguel ★★★★★
()
Ответ на: комментарий от Miguel

>Единственное, что требуется при использовании шаблона - поддержка нужного интерфейса

Да, в теории это замечательно. На практике же авторы дженериков выбирают избыточный интерфейс. Т.е. интерфейс требует методы a(), b() и c(), а реально нужны только a() и c(), но оптимальный интерфейс описывать лень. Соответственно юзерам дженериков приходится описывать этот нахрен не нужный b(). Для джавы тормознутость и многословность - норма жизни, а в c++ это неприемлимо.

Одна из причин выкидывания концетов из c++0x в том, что они потребуют просто астрономических объёмов писанины для правильного использования, а профита - шиш.

legolegs ★★★★★
()
Ответ на: комментарий от legolegs

многословность - норма жизни, а в c++ это неприемлимо.

C++ очень многословен.

а профита - шиш.

Угу, потому что фокусов типа того, что я показывал, всё равно не будет.

Miguel ★★★★★
()
Ответ на: комментарий от legolegs

а профита - шиш.

И, кстати, такой вещи, как реализация шаблонов в cpp вместо h - тоже не будет. Легаси, мать её...

Miguel ★★★★★
()
Ответ на: комментарий от Miguel

> И, кстати, такой вещи, как реализация шаблонов в cpp вместо h - тоже не будет

вас сильно напрягает, что реализация будет лежать в файле с расширением .h, а не .cpp?

lester ★★★★
()
Ответ на: комментарий от lester

вас сильно напрягает, что реализация будет лежать в файле с расширением .h, а не .cpp?

Меня сильно напрягает невозможность разделить интерфейс и реализацию.

Miguel ★★★★★
()
Ответ на: комментарий от Miguel

> Меня сильно напрягает невозможность разделить интерфейс и реализацию.

как я уже говорил - разница будет только в расширении файла, вынести реализацию никто не мешает

lester ★★★★
()
Ответ на: комментарий от lester

как я уже говорил - разница будет только в расширении файла, вынести реализацию никто не мешает

Мешает. Или приводи пример.

Miguel ★★★★★
()
Ответ на: комментарий от Miguel

> Угу, потому что фокусов типа того, что я показывал, всё равно не будет.

Типы стереть руками можно всегда (и возможно даже обойтись без void). Может не поленюсь и запощу пример.

www_linux_org_ru ★★★★★
()
Ответ на: комментарий от www_linux_org_ru

Типы стереть руками можно всегда

Никто не спорит, что на одном тьюринг-полном языке всегда можно сделать то, что делается на другом. Цимес в другом: концепты давали возможность НАЛОЖИТЬ ограничения, но одновременно давали очень мало возможностей ими ПОЛЬЗОВАТЬСЯ. Поэтому они, собственно, и не нужны.

Miguel ★★★★★
()
Ответ на: комментарий от Miguel

(Всякие там quux нихрена не понятные названия)

Вот элементарное стирание типов по твоему примеру.

Конечно, более похоже на яву было бы *не* делать ErasedCons предком Cons (чтобы его можно было определить *после* определения Cons), но это требует библиотечки, которую до сих пор мне лень написать.

#include <iostream>

class ErasedCons
{
public:
  virtual void print()=0;
};

class Nil: public ErasedCons 
{
  virtual void print() { std::cout << "Nil\n"; }
};

template<class T> class Cons: public ErasedCons
{
public:
  Cons(T* tail, int payload): tail(tail), payload(payload) {}
  virtual void print() { std::cout << "Cons: " << payload << ",\n"; tail->print(); }
private:
  T* tail;
  int payload;
};
template<class T> ErasedCons* cons(T* tail, int payload) { return new Cons<T>(tail, payload); }

ErasedCons* erased_cons(int i)
{
  ErasedCons* result = new Nil();
  for( int j=1; j<=i; j++) {
    result = cons(result,j*j);
  }
  return result;
}

int main(int argc, char** argv)
{
  int i;
  std::cin >> i;
  ErasedCons* ec = erased_cons(i);
  ec->print();
  return 0;
}
www_linux_org_ru ★★★★★
()
Ответ на: комментарий от Miguel

> Цимес в другом: концепты давали возможность НАЛОЖИТЬ ограничения, но одновременно давали очень мало возможностей ими ПОЛЬЗОВАТЬСЯ. Поэтому они, собственно, и не нужны.

Это я не понял.

Ладно, вот в моем примере все стирание типов свелось к ": public ErasedCons", и сами классы Cons и Nil *не* пришлось редактировать, но в более сложном случае может быть придется. хз.

www_linux_org_ru ★★★★★
()
Ответ на: комментарий от www_linux_org_ru

все-таки пришлось редактировать вне функции print, а виртуальная функция print таки добавилась во внутрь, но этого можно избежать через библиотечку-виртуализатор

www_linux_org_ru ★★★★★
()
Ответ на: комментарий от Miguel

> Ну и что ты хотел этим показать?

Ты писал:

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

Я тебе написал аналогичный пример, который не падает.

www_linux_org_ru ★★★★★
()
Ответ на: комментарий от www_linux_org_ru

Цимес в другом: концепты давали возможность НАЛОЖИТЬ ограничения, но одновременно давали очень мало возможностей ими ПОЛЬЗОВАТЬСЯ. Поэтому они, собственно, и не нужны.

Это я не понял.

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

По-прежнему невозможно отдельно скомпилить код шаблона и код, использующий шаблон.

По-прежнему при написании кода шаблона известные ограничения на его аргументы не страхуют программиста.

Miguel ★★★★★
()
Ответ на: комментарий от Miguel

quux это у тебя что ли get_depth() ?

я вместо него сделать print — оно нагляднее

www_linux_org_ru ★★★★★
()
Ответ на: комментарий от Miguel

> По-прежнему компилятор может отказаться работать с тем, что соответствует специфицированному интерфейсу

Это более сложный вопрос, чем кажется с «теоретических» позиций — отнюдь не всегда полезно сначала специфицировать интерфейс, а затем проверять эту спецификацию.

Хотя иногда это надо.

По-прежнему невозможно отдельно скомпилить код шаблона и код, использующий шаблон.

я завтра видимо отвечу, сорри

www_linux_org_ru ★★★★★
()
Ответ на: комментарий от www_linux_org_ru

Я тебе написал аналогичный пример, который не падает.

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

Кстати, зачем в твоём коде шаблоны - непонятно совсем. Проще было так:

#include <iostream> 
class ErasedCons { 
public:
  virtual void print()=0; 
}; 
class Nil: public ErasedCons { 
  virtual void print() { std::cout << "Nil\n"; } 
}; 
class Cons: public ErasedCons 
{ 
public: 
  Cons(ErasedCons *_tail, int _payload): tail(_tail), payload(_payload) {} 
  virtual void print() { std::cout << "Cons: " << payload << ",\n"; tail->print(); } 
private: 
  ErasedCons *tail; 
  int payload; 
};
ErasedCons *cons(ErasedCons *tail, int payload) { return new Cons(tail, payload); } 
ErasedCons *erased_cons(int i) 
{ 
  ErasedCons *result = new Nil(); 
  for( int j=1; j<=i; j++) { 
    result = cons(result,j*j); 
  } 
  return result; 
} 
 
int main(int argc, char** argv) 
{ 
  int i; 
  std::cin >> i; 
  ErasedCons *ec = erased_cons(i); 
  ec->print(); 
  return 0; 
}
Это я руками развернул шаблоны у тебя. Получилось проще и понятнее.

Miguel ★★★★★
()
Ответ на: комментарий от Miguel

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

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

Поставь уж задачу. Вот я например могу предложить что-то такое, (по идее из «равенство длин массивов»), но по-практичнее:

template<unsigned int n> class Array
{
public:
  const unsigned int depth=n;
  ...
  Array<n+1>* push(double value) { return new Array<n+1>(value, this);}
private:
  double values[n];
};

теперь предположим, что мы хотим сделать ArrayOfAnySize, который был бы тем же Array, но со статически неизвесным n, и при этом хотим избежать копипаста в него методов из Array

возможно ArrayOfAnySize сделать предком всех Array<n>, но лучше и этого избежать — идеально было бы вообще не трогать код Array<n>

www_linux_org_ru ★★★★★
()
Ответ на: комментарий от www_linux_org_ru

теперь предположим, что мы хотим сделать ArrayOfAnySize, который был бы тем же Array, но со статически *постоянным, но неизвесным* n, и при этом хотим избежать копипаста в него методов из Array

www_linux_org_ru ★★★★★
()
Ответ на: комментарий от www_linux_org_ru

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

Да, естественно. Спросили про разницу между шаблонами и дженериками. Если переписать данный пример буква в букву на C++, используя шаблоны - он не заработает. Разница видна.

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

Miguel ★★★★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.