шаблоны — есть ли настоящий статический полиморфизм в C ++?

Вот простой код на C ++:

#include <iostream>
#include <typeinfo>

template<typename T>
void function()
{
std::cout << typeid(T).name() << std::endl;
}

int main()
{
function<int>();
function<double>();
return 0;
}

Я прочитал, что шаблоны в C ++ время компиляции особенность, которая не похожа на дженерики в C # / Java.

Итак, как я понял, компилятор C ++ разделит одну определенную функцию на различное количество (зависит от количества вызовов с различным типом) функций.

Я прав или нет? Я не специалист по компиляторам C ++, поэтому прошу у вас совета.

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

Потому что кажется, что это не переопределение, а просто вызов копии из исполняемого файла или … не имеет значения, что приложение имеет в выходном двоичном образе, но только важная часть находится на уровне кода C ++, и я не буду смотреть на это. как компилятор производит вывод.

13

Решение

Есть ли настоящий статический полиморфизм в C ++?

Абсолютно — существует три механизма статического полиморфизма: шаблоны, макросы и перегрузка функций.

Итак, как я понял, компилятор C ++ разделит одну определенную функцию на различное количество (зависит от количества вызовов с различным типом) функций. Я прав или нет?

Это общая идея. Количество функций, которые создаются, зависит от количества перестановок параметров шаблона, которые могут быть явно указаны, как в function<int> а также function<double> или — для шаблонов, которые используют параметры шаблона для сопоставления с аргументами функции — автоматически выводятся из аргументов функции, например:

template <typename T, size_t N>
void f(T (&array)[N])
{ }

double d[2];
f(d);   // instantiates/uses f<double, 2>()

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


Я хочу знать, могу ли я описать код выше как статический полиморфизм?

На самом деле, нет.

  • template<> function создается для двух типов

    • принципиально, полиморфизм не используется, чтобы выбрать, какой из двух экземпляров function для отправки на сайты вызова

    • тривиально, во время таких реализаций typeid(T) оценивается для int а также double а также эффективно ведет себя полиморфно с точки зрения программиста (хотя это ключевое слово компилятора — реализация неизвестна)

  • тривиально, сочетание статического и номинально динамического (но здесь, вероятно, оптимизируемого до статического) полиморфизма поддерживает ваше использование std::cout

Фон — полиморфизм и генерация кода

Требование, которое я считаю крайне важным для полиморфизм является:

  • когда код скомпилирован (будь то «нормальный» код или по экземпляру шаблона или подстановке макроса), компилятор автоматически выбирает (создает при необходимости) — и встраивает, или вызывает — отличное поведение, соответствующее типу (машинный код)

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

    • например, std::cout << x; полиморфно вызывает другой код как тип x варьируется, но все еще выводит xзначение, в то время как неполиморфный printf("%d", x) ручки intс, но должен быть вручную изменен на printf("%c", x); если x становится char,

Но то, что мы пытаемся достичь с помощью полиморфизма, является более общим:

  • повторное использование алгоритмического кода для нескольких типов данных без встраивания явного обнаружения типов и кода ветвления

    • то есть без исходного кода программы, содержащего if (type == X) f1(x) else f2(x);код стиля
  • уменьшенное бремя обслуживания так как после явного изменения типа переменной требуется меньше последующих изменений вручную в исходном коде

Эти большие аспекты изображения поддерживаются в C ++ следующим образом:

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

    • фактически известный как «создание экземпляров» для шаблонов и «замена» для макросов препроцессора, но для удобства я буду использовать «создание экземпляров» в дальнейшем; концептуально, перекомпиляция или реинтерпретация …
  2. неявная отправка (статический или динамический) для отличное поведение (машинный код), соответствующий отдельный тип (ы) обрабатываемых данных.

…и в некоторых незначительных отношениях в соответствии с моим ответом на Полиморфизм в с ++

Различные типы полиморфизма включают один или оба из них:

  • отправка (2) может случиться во время реализации (1) для шаблоны и препроцессор макрос,

  • конкретизации (1) обычно бывает во время отправки (2) для шаблоны (без соответствующей полной специализации) и функциональная макрос (циклично, хотя макросы не расширяются рекурсивно)

  • отправка (2) может случиться без конкретизации (1) когда компилятор выбирает уже существующую функцию перегрузка или же специализация шаблона, или когда сработает компилятор виртуальный/ динамическая отправка.

Что на самом деле использует ваш код?

function<int> а также function<double> повторно использовать function шаблон кода для создания отдельного кода для каждого из этих типов, так что вы являются получение конкретизации (1) как указано выше. Но вы жестко программируете, какой экземпляр вызывать, вместо того, чтобы компилятор неявно выбирал экземпляр в зависимости от типа какого-либо параметра, т.е. не напрямую использовать неявную диспетчеризацию ala (2) при вызове function, В самом деле, function отсутствует параметр, который компилятор мог бы использовать для неявного выбора экземпляра шаблона.

Один экземпляр (1) не достаточно рассмотреть ваш код, чтобы использовать полиморфизм. Еще, Вы достигли удобного повторного использования кода.

Так что же будет однозначно полиморфным?

Чтобы проиллюстрировать, как шаблоны могут поддерживать dispatch (2), а также instantiation (1) и бесспорный обеспечить «полиморфизм», рассмотрим:

template<typename T>
void function(T t)
{
std::cout << typeid(T).name() << std::endl;
}

function(4);      // note: int argument, use function<int>(...)
function(12.3);   // note: double argument, use function<double>(...)

Приведенный выше код также использует неявная отправка в соответствующий типу код — аспект «2» выше — полиморфизм.


Не типовые параметры

Интересно, что C ++ предоставляет возможность создания экземпляров шаблонов с интегральными параметрами, такими как логические, int и константы указателя, и использовать их для любых действий без изменения типов данных и, следовательно, без какого-либо участия в полиморфизме. Макросы еще более гибкие.


Обратите внимание, что использование шаблона в C.R.T.P. стиль НЕ требование статического полиморфизма — это пример его применения. Во время реализации компилятор демонстрирует статический полиморфизм при сопоставлении операций с реализациями в типе, заданном параметром.


Обсуждение терминологии

Получить окончательное определение полиморфизма сложно. Википедия цитирует онлайн-глоссарий Бьярна Страуструпа, «обеспечивающий единый интерфейс для сущностей разных типов»: это подразумевает struct X { void f(); }; struct Y { void f(); }; уже проявляется полиморфизм, но ИМХО мы получаем полиморфизм только тогда, когда мы использование соответствие интерфейса из клиентского кода, например template <typename T> void poly(T& t) { t.f(); } требует статической полиморфной отправки t.f() для каждого экземпляра.

13

Другие решения

В Википедии перечислены три типа полиморфизма:

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

  • Если код написан без упоминания какого-либо конкретного типа и, таким образом, может прозрачно использоваться с любым количеством новых типов, это
    называется параметрический полиморфизм. В объектно-ориентированном программировании
    Сообщество, это часто называют обобщением или обобщенным программированием. В
    сообщество функционального программирования, это часто просто называют
    полиморфизм.

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

Первый относится к перегрузке функций. Третий тип относится к позднему связыванию или полиморфизму во время выполнения, который вы увидите, например, в наследовании. Второе — это то, что нас интересует.

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

Например:

template <typename T, typename U>
auto func(const T& t, const U& u) -> decltype(t + u)
{
return (t + u);
}

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

Тем не менее, в вашем примере у вас есть экземпляры для ваших функций, которые являются различными, function<int> а также function<double>, Вот цитата:

Чтобы быть полиморфным, [a ()] должен иметь возможность работать со значениями в
как минимум два разных типа (например, int и double), поиск и выполнение
код, соответствующий типу.

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

5

В вашем примере нет статического полиморфизма, потому что нет полиморфизма. Это потому что function<int>() не выглядит так же, как function<double>(),

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

#include <iostream>
#include <typeinfo>

template<typename T>
void function(T)
{
std::cout << typeid(T).name() << std::endl;
}

int main()
{
function(0);   // T is int
function(0.0); // T is double
return 0;
}

Вот еще один пример:

template<typename T>
void function(T t)
{
t.foo();
}

struct Foo()
{
void foo() const {}
};

struct Bar()
{
void foo() const {}
};

int main()
{
Foo f;
Bar b;
function(f); // T is Foo
function(b); // T is Bar
}
4

Для с ++ термин «статический полиморфизм» обычно используется, например, для CRTP Типовые шаблоны дизайна:

template<typename Derived>
class Base
{
void someFunc() {
static_cast<Derived*>(this)->someOtherFunc();
};
};

class ADerived : public Base<ADerived>
{
void someOtherFunc() {
// ...
}
};

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

2

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

template<class T>
class Base
{
public:
int a() { return 7; }
};

template<>
class Base<int>
{
public:
int a() { return 42; }
};

template<>
class Base<double>
{
public:
int a() { return 121; }
};

Здесь мы видим, что для большинства классов a () вернет 7; Специализированные (производные) экземпляры для int а также double может иметь радикально различное поведение, демонстрируемое в простом случае различными возвращаемыми значениями, то же самое может быть сделано для шаблонов, например, с параметрами int, и может демонстрировать то, что может быть странным образом названо статическим рекурсивным полиморфизмом.

Хотя срок polymorphic возможно растягивается, концепция определенно есть. Чего не хватает, так это не способности переопределять функции, а способности специализированных классов автоматически наследовать функции, которые не меняют поведение.

0
По вопросам рекламы [email protected]