LINUX.ORG.RU

Шаблоны реализуются препроцессором. Дженерики --- нет. Все остальные различия проистекают отсюда.

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

>препроцессором

мне кажется вы ошибаетесь

различия

какие именно? в чем преимущество дженериков?

antony986
() автор топика
Ответ на: комментарий от const86

Надо же такое сморозить.

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

NightmareZz
()

Шаблон происходит от слова шаблон. Это собственно говоря класс или функция, по которым компилятор может генерировать классы или функции. Например в С++ это должно быть в хедере.

Дженерики это универсальные классы в чистов виде. Они ничем не отличаются особо от неуниверсальных, только экземпляры дженерика создаются на основе типа или нескольких типов.

Как то сумбурно вышло, но почему бы не прогуглить?

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

Как то сумбурно вышло, но почему бы не прогуглить?

ну я этим занимаюсь последние два часа, пока только вижу примеры полностью аналогичные шаблонам из c++. зачем то же их придумали, должна же быть killer-feature.

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

#include<iostream>

using namespace std;

template < class T > 
class Test : public T
{
    public:
        void Test1()
        {
            cout<<"Test1"<<endl;
        };
};

class Papa
{
    public:
        void Papa1()
        {
            cout<<"Papa1"<<endl;
        };
};

class Mama
{
    public:
        void Mama1()
        {
            cout<<"Mama1"<<endl;
        };
};

void main()
{
    Test<Papa> testPapa;
    testPapa.Papa1();
    testPapa.Test1();

    Test<Mama> testMama;
    testMama.Mama1();
    testMama.Test1();
}
antony986
() автор топика
Ответ на: комментарий от antony986

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

А в чём смысл такой штуки?

NightmareZz
()

В С++ шаблоны раскрываются на этапе компиляции и для каждого типа создаётся отдельная функция. В джаве используется только одна функция, все шаблонные объявления заменяются на тип Object и везде расставляются проверки.

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

> Например в С++ это должно быть в хедере.

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

anonymous
()

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

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

Отсюда разнообразные особенности. Например:

1) Шаблоны C++ не поддерживают раздельной компиляции. Нельзя откомпилировать файл с шаблоном, после чего инклудить только заголовок с интерфейсом; если шаблон используется в программе, этой программе обязательно должна быть доступна его реализация.

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

2) Шаблоны поддерживают частичную специализацию. Дженерики - нет. Причина именно та: дженерик-функция компилируется в ОДНУ функцию, которая не зависит от типа-аргумента. Шаблон же кто угодно может частично специализировать без ведома автора, не изменяя основной код.

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

http://migmit.vox.com/library/post/поигрался-тут.html

Соответствующая тема на ЛОРе куда-то делась.

4) Шаблоны могут сильно замедлить компиляцию. Именно потому, что каждый из них нужно развернуть столько раз, сколько он используется. Дженерики компилируются один раз.

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

>1) Шаблоны C++ не поддерживают раздельной компиляции. Нельзя откомпилировать файл с шаблоном, после чего инклудить только заголовок с интерфейсом; если шаблон используется в программе, этой программе обязательно должна быть доступна его реализация.

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

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

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

3) Шаблоны не поддерживают случаи, когда количество необходимых развёртываний не известно заранее, а определяется в рантайме.

о, спасибо, что-то вроде этого я и искал

4) Шаблоны могут сильно замедлить компиляцию. Именно потому, что каждый из них нужно развернуть столько раз, сколько он используется. Дженерики компилируются один раз

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

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

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

man C++0x

Шаблоны могут сильно замедлить компиляцию


man pch

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

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

Может быть, вы не поняли, может быть, я.

Что понимается под экспортом шаблонов?

в принципе если есть нужда наложить ограничения на аргументы шаблона

Нет, зачем?

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

??? Почему?

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

> man C++

это не повод не замечать будущий стандарт( во многом реализованный )

Не особо поможет.


отлично помогает - у меня 2000 файлов за 3 мин полностью пересобирается

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

это не повод не замечать будущий стандарт( во многом реализованный )

Тогда делись подробностями.

отлично помогает - у меня 2000 файлов за 3 мин полностью пересобирается

Вопрос не в количестве файлов, а в количестве инстанцированных шаблонов.

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

> Тогда делись подробностями.

«extern template class ...»

это запретит инстанциацию шаблона и позволит использовать внешнюю реализацию

Вопрос не в количестве файлов, а в количестве инстанцированных шаблонов.


большинство из них будет инстанцироваться из описания в заголовках, и при желании можно явно прописать инстанциации через «template class» для использования в pch

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

это запретит инстанциацию шаблона и позволит использовать внешнюю реализацию

Замечательно. Теперь вопрос. Есть шаблон. Скажем,

template<class T> class TMPL {
public:
  T *smth;
  void print() {std::cout << *smth << std::endl;}
}

Есть два места, где он используется.

TMPL<int> intTmpl;
TMPL<float> floatTmpl;
...
intTmpl.print();
floatTmpl.print();

Вопрос. Последние две строчки скомпилируются в вызов одной и той же функции - или разных? Могу ли я вынести реализацию print() в отдельный файл, скомпилировать его, а затем выкинуть исходник, но так, чтобы всё по-прежнему работало?

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

> Последние две строчки скомпилируются в вызов одной и той же функции - или разных?

разных

Могу ли я вынести реализацию print() в отдельный файл, скомпилировать его, а затем выкинуть исходник, но так, чтобы всё по-прежнему работало?


нет - нужна реализация для каждой инстанциации

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

разных

нет - нужна реализация для каждой инстанциации

О чём и разговор.

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

А насколько много инстанцирований шаблонов?

В этих примерах очень много инстанцирований, поэтому время компиляции большое и памяти компилятору требуется очень много. И PCH этому как-то не очень помогают.

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

> у меня 2000 файлов за 3 мин полностью пересобирается

Для не C/C++ это медленно. По скорости компиляции шаблоны должны сливать генерикам по определению.

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

функция то одна, но внутри будет куча проверок, наделанных компилятором, на типы, нет?

Нет, конечно. Во всяком случае, я не вижу причины это делать. Это же не dynamic typing.

Другой вопрос, что подобная дженерик-функция, возможно, не будет очень уж хорошо оптимизирована. Если при компиляции дженерика точно знать, какой тип в него придёт, можно соптимизировать лучше (и, полагаю, C++-компиляторы это делают; по крайней мере, они это МОГУТ делать). Но проверки вставлять особо незачем.

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

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

Дотнетовский компилятор JIT это и делает. Там можно параметризовать генерики по типам-значениям. В конечном итоге получается примерно как при инстанцировании шаблона в C++ - создается в нативном коде отдельная версия генерика для каждого типа-значения :)

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

функция то одна, но внутри будет куча проверок, наделанных компилятором, на типы, нет?

Нет, конечно. Во всяком случае, я не вижу причины это делать. Это же не dynamic typing.

Конкретно в джаве проверки будут. Например код

List<Integer> l;
...
Integer i = l.get(0);
будет откомпилирован как
=java]
Object o = l.get(0);
Integer i = (Integer) o; // здесь рантайм проверка

Но это, конечно, проблема джавы а не генериков, и, возможно, JIT это соптимизирует.

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

> В джаве используется только одна функция, все шаблонные объявления заменяются на тип Object и везде расставляются проверки.

не всегда на, и чаще всего не на Object, а на какой-то тип, описанный в дженерике (возможно и Object тоже)

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

раз ты тут появился, давай обсудим пример для зависимых типов:

есть класс array, которые создаются с фиксированной, но определяемой по вводу от пользователя длинной; есть операция +, которая конкатенирует два массива, длины складываются; есть scalar_product, работающий только на массивах равных длин

так вот, компилятор должен разрешать например

(a+b+c+d+e).scalar_product(a+e+d+b+c)

при этом д-во равенства длин должен он получать либо сам, либо оно создается пользователем

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

> раз ты тут появился, давай обсудим пример для зависимых типов:

Да.. что-то давно ваших тем не было ;)

dave ★★★★★
()
Ответ на: комментарий от Miguel
LinkedList<Integer> l = new LinkedList<Intger>();
LinkedList l2 = l;
LinkedList<String> l3 = (LinkedList<String>) l2;
l3.add("hello");
Integer i = l.get(0);

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

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

Не расставляются. Ради прикола - зацепись дебаггером и положи в List<Integer> например String в рантайме. Никаких проблем.

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

Этот код у меня скомпилировался в такое:

   0:   new     #2; //class java/util/LinkedList
   3:   dup
   4:   invokespecial   #3; //Method java/util/LinkedList."<init>":()V
   7:   astore_1
   8:   aload_1
   9:   astore_2
   10:  aload_2
   11:  astore_3
   12:  aload_3
   13:  ldc     #4; //String hello
   15:  invokevirtual   #5; //Method java/util/LinkedList.add:(Ljava/lang/Object;)Z
   18:  pop
   19:  aload_1
   20:  iconst_0
   21:  invokevirtual   #6; //Method java/util/LinkedList.get:(I)Ljava/lang/Object;
   24:  checkcast       #7; //class java/lang/Integer
   27:  astore  4
Проверка в 27-й инструкции.

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

Если что, они расставляются именно там, где идёт преобразование из шаблонного типа в «реальный». Т.е. в присваивании результата метода LinkedList.get (который на самом деле возвращает Object) переменной i.

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

LinkedList l2 = l;

А, ну, если уёбищное легаси юзать - тогда да, там можно многое наворотить, согласен.

Вообще говоря, давно надо было запретить подобные вещи.

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

На get() все так. Будет ClassCastException. Я имел в виду, что в типизированную коллекцию на самом деле можно положить все, что угодно.

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

> 3) Шаблоны не поддерживают случаи, когда количество необходимых развёртываний не известно заранее, а определяется в рантайме. Они полностью разворачиваются при компиляции. Дженерики поддерживают, так как, собственно, не разворачиваются вообще. Пример, когда это играет:

http://migmit.vox.com/library/post/поигрался-тут.html


не понял логики приведённого C++ кода. Какая-то фигня, семантически.

Что мешает сделать как с факториалом, шаблонами? факториал шаблонами разворачивается в конечном итоге в 1/0, аналогами которых выступает sizeof от члена класса. А мы сделаем аналог 1/0 — «код, который компилируется» или «код, который не компилируется из-за ошибки» (можно прагму error вставить).

Шаблоны в С++ — довольно мощный язык, почти функциональный: см. http://bartoszmilewski.wordpress.com/2009/10/21/what-does-haskell-have-to-do-... http://bartoszmilewski.wordpress.com/2009/10/26/haskellc-video-and-slides/ или HN0 http://nponeccop.livejournal.com/104455.html http://nponeccop.livejournal.com/104884.html http://nponeccop.livejournal.com/105451.html (всё на http://nponeccop.livejournal.com/tag/hn0 )

3) Шаблоны не поддерживают случаи, когда количество необходимых развёртываний не известно заранее, а определяется в рантайме.


можно пример, когда без этого ну никак не обойтись? пример с вектором не катит — можно обойтись простыми шаблонами, а можно и сделать что-то вроде системы типов и Хиндли-Милнера, фактически, компилятор (вроде того же HN0).

То есть, заменяем такой пример с рантаймом на рефлексию + использование компилятора через рефлексию + система типов.

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

>> отлично помогает - у меня 2000 файлов за 3 мин полностью пересобирается

Вопрос не в количестве файлов, а в количестве инстанцированных шаблонов


да-да. Любопытные могут пособирать на время тот же boost, spirit, код с препроцессором вроде qt/moc и код без шаблонов.

anonymous
()
Ответ на: комментарий от kamre

> А сколько собирается example mini_c из boost::spirit или boost::wave?

Долго оно собирается. Я на своей Gentoo померял — что компилятор D, что Go (gc, не gccgo, хотя и gccgo довольно быстро, если 4.5 уже собранный — плагин всё-таки), что какой-нибудь компилятор на LLVM (например, ldc, pure) — не говоря уже о компиляторах на lemon, antlr, ragel, lpeg — быстрее гораздо.

anonymous
()
Ответ на: комментарий от kamre

> В этих примерах очень много инстанцирований, поэтому время компиляции большое и памяти компилятору требуется очень много. И PCH этому как-то не очень помогают.

а как он может помогать, когда разные инстанцирования разворачиваются в разный код? тот же совет по precompiled headers в основном сводится к тому, чтобы ограничить количество таких случаем разумным минимумом, а тут misuse препроцессора, особенно boost::spirit

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

не понял логики приведённого C++ кода. Какая-то фигня, семантически.

А логику C#-ного кода - понял?

Что мешает сделать как с факториалом, шаблонами?

Не понял, что имеется в виду.

Шаблоны в С++ — довольно мощный язык, почти функциональный

Спасибо, К.О.

можно пример, когда без этого ну никак не обойтись?

Можно. http://migmit.vox.com/library/post/поигрался-тут.html

пример с вектором не катит — можно обойтись простыми шаблонами

Как?

То есть, заменяем такой пример с рантаймом на рефлексию + использование компилятора через рефлексию + система типов.

Чего?

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

>> у меня 2000 файлов за 3 мин полностью пересобирается

Для не C/C++ это медленно. По скорости компиляции шаблоны должны сливать генерикам по определению.


+1. C++ собирается вообще медленно по сравнению с более другими языками.

$ cd gc-go/src; find . -name '*.go' -or -name '*.c*' -print |wc -l
335

$cd dmd2/src; find . -name '*.c*' -print |wc -l
157

и тот, и другой собираются <=3 минут на моей машине, но Go даже быстрее, несмотря на то, что файлов больше

Это компиляторы, сам код на этих языках собирается ощутимо быстрее C++

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

А логику C#-ного кода - понял?

ага.

Что мешает сделать как с факториалом, шаблонами?

Не понял, что имеется в виду.

я про хак вроде такого: http://cpptruths.blogspot.com/2005/11/c-templates-are-turing-complete.html http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.120.2003&rep=rep1&type=pdf

google:// «Source: C++ Templates are Turing Complete by Todd L. Veldhuizen» google://«Five compilation models for C++ templates (Extended Abstract) (2007)»

http://en.scientificcommons.org/todd_l_veldhuizen

Как вообще на C++ реализуется рефлексия ? http://www.vollmann.ch/pubs/meta/meta/meta.html

И факториал, и этот пример с

template<int Depth, int A, typename B>
struct K17 { 
static const int x =
K17 <Depth+1, 0, K17<Depth,A,B> >::x
+ K17 <Depth+1, 1, K17<Depth,A,B> >::x
+ K17 <Depth+1, 2, K17<Depth,A,B> >::x
+ K17 <Depth+1, 3, K17<Depth,A,B> >:x
+ K17 <Depth+1, 4, K17<Depth,A,B> >::x;
};
template <int A, typename B>
struct K17 <16,A,B> { static const int x = 1;
};
static const int z = K17 <0,0,int>::x;
int main(void) { }

по сути одинаковы — в хвосте рекурсии подставляется единица, во всех остальных случаях — след. шаг рекурсии

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

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

>> пример с вектором не катит — можно обойтись простыми шаблонами

Как?


имеем: код, который должен компилироваться корректно, если длины векторов совпадают, или не компилироваться, если не совпадают.
Хвост рекурсии: код, который компилируется в ошибку, или в 1.
Тело рекурсии: свёртка по + с 0 след. шага рекурсии.
Длины векторов совпадают => хвосты компилируются в 1, свёртка компилируется в N, код компилируется в шаблон, включая N.
Длины различны => хвост компилируется в ошибку, свёртка не компилируется, ловим ошибку времени компиляции при разворачивании шаблона.

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