2013-03-24 5 views
1

У меня есть два вопроса о шаблонах в C++. Предположим, что я написал простой список, и теперь я хочу использовать его в своей программе для хранения указателей на разные типы объектов (A *, B * ... ALot *). Мой коллега говорит, что для каждого типа будет создан выделенный фрагмент кода, хотя все указатели фактически имеют одинаковый размер.Будет ли компилятор C++ генерировать код для каждого типа шаблона?

Если это правда, может кто-нибудь объяснить мне, почему? Например, в Java generics имеют ту же цель, что и шаблоны для указателей в C++. Дженерики используются только для проверки перед компиляцией и удаляются до компиляции. И, конечно, один и тот же байтовый код используется для всего.

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

Если это имеет значение, мы говорим о встроенных приложениях.

Я нашел подобный вопрос, но не полностью ответить на мой вопрос: Do C++ template classes duplicate code for each pointer type used?

Спасибо большое!

+8

как он не полностью ответил на ваш вопрос? Что он не ответил? – imulsion

+1

'char' и' short' ** не ** имеют одинаковый размер, 'char' - 1 байт,' short' - 2 байта. – antonijn

+2

Java Generics практически не имеют ничего общего с шаблонами C++. Возможно, кроме синтаксиса '<>. – Mat

ответ

4

У меня есть два вопроса о шаблонах в C++. Предположим, что я написал простой список, и теперь я хочу использовать его в своей программе для хранения указателей на разные типы объектов (A *, B * ... ALot *). Мой коллега говорит, что для каждого типа будет создан выделенный фрагмент кода, хотя все указатели фактически имеют одинаковый размер.

Да, это эквивалентно тому, что обе функции записаны.

Некоторые линкеры обнаруживают идентичные функции и устраняют их. В некоторых библиотеках известно, что их компоновщик не имеет этой функции и учитывает общий код в одной реализации, оставляя только обтекающую оболочку вокруг общего кода. То есть, специализация std::vector<T*> может перенаправить все работы на std::vector<void*>, а затем выполнить кастинг на выходе.

Теперь сложная складчатость сложна: относительно легко сделать функции, которые, по вашему мнению, идентичны, но в конечном итоге не являются одинаковыми, поэтому генерируются две функции. В качестве примера игрушек вы можете уйти и напечатать имя типа через typeid(x).name(). Теперь каждая версия функции различна, и их нельзя устранить.

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

Если это правда, может кто-нибудь объяснить мне, почему? Например, в Java generics имеют ту же цель, что и шаблоны для указателей в C++. Дженерики используются только для проверки на компиляцию и удаляются до компиляции. И, конечно, один и тот же байтовый код используется для всего.

Нет, они не являются. Дженерики примерно эквивалентны методу стирания типа C++, например, std::function<void()> для сохранения любого вызываемого объекта. В C++ стирание типа часто выполняется с помощью шаблонов, но не все использование шаблонов - стирание стилей!

То, что C++ делает с шаблонами, которые по сути не стираются, как правило, невозможно сделать с генериками Java.

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

Второй вопрос заключается в том, чтобы выделенный код также был сгенерирован для char и short (учитывая, что оба они имеют одинаковый размер, и специализации нет).

Это разные виды. Я могу написать код, который будет вести себя по-другому с значением char или short. В качестве примера:

std::cout << x << "\n"; 

с й будучи short, эта печать целого числа, значение которого x - с x будучи char, это выводит символ, соответствующий x.

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

Если это имеет значение, мы говорим о встроенных приложениях.

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

5

Ответ может быть. В общем, каждое создание шаблона является уникальным типом с уникальной реализацией, а приведет к совершенно независимому экземпляру кода. Слияние экземпляров возможно, но будет считаться «Оптимизация» (в соответствии с правилом «как будто»), и эта оптимизация не имеет широкого распространения.

Что касается сравнений с Java, есть несколько точек , чтобы иметь в виду:

  • C++ использует семантику значений по умолчанию. Пример std::vector, для примера , фактически вставляет копии. И независимо от того, являетесь ли вы , копирование short или double действительно имеет значение в генерации кода . В Java short и double будут помещены в бокс, , и сгенерированный код каким-то образом клонирует экземпляр в коробке; клонирование не требует разного кода, поскольку он вызывает виртуальную функцию Object, но физическое копирование делает.

  • C++ намного мощнее, чем Java. В частности, он позволяет сравнивать такие вещи, как адрес функций, и требует , что функции в разных экземплярах шаблонов имеют разных адресов. Обычно это не важный момент, , и я могу легко представить себе компилятор с опцией, которая сообщает , чтобы игнорировать эту точку и объединить экземпляры, которые идентичны на двоичном уровне. (Я думаю, что VC++ есть что-то вроде этого .)

Другая проблема заключается в том, что реализация шаблона в C++ должен присутствовать в файле заголовка. В Java, конечно, все должно присутствовать всегда, поэтому эта проблема затрагивает все классы , а не только шаблон. Это, конечно, одна из причин, почему Java не подходит для больших приложений.Но это означает, что вам не нужна сложная функциональность в шаблоне ; это теряет одно из основных преимуществ C++, по сравнению с Java (и многими другими языками). На самом деле, это не редко, при реализации сложной функциональности в шаблонах, , чтобы наследовать шаблон из класса без шаблона, который выполняет большую часть реализации в терминах void*. В то время как реализация больших блоков кода с точки зрения void* никогда не была потеха, она имеет то преимущество, что предлагает лучшее из миров для клиента: реализация скрыта в скомпилированных файлах , невидимых в любом случае, форме или манере для клиента.

+0

Привет, Джеймс, большое спасибо за ваш ответ! Я могу спорить о том, что Java не подходит для больших приложений :-), но я не буду. Мой вопрос касался только списка шаблонов и других контейнеров, которые не содержат абсолютно никакой логики, которая могла бы манипулировать этими объектами, кроме как хранить их. Если вы скажете, что точно такой же двоичный код может быть объединен, я счастлив. –

+0

@YuriyKulikov Я говорю, что он может быть объединен под правилом «как будто», а не с тем, что он будет объединен. Для таких вещей, как 'std :: list', код является встроенным и тривиальным; после инкрустации, вероятно, нечего будет слить. (Это, конечно, то, что делает JIT-компилятор на Java. Формально создание шаблона Java полна преобразований типов, которые соответствуют «dynamic_cast» на C++ и довольно дороги во время выполнения. JIT сможет устранить большинство, если не все проверенные конверсии, и сделать эквивалент 'static_cast'.) –

Смежные вопросы