Есть реальный статический полиморфизм в 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++, и я не должен смотреть, как компилятор производит вывод.

5 ответов


есть реальный статический polymorhism в 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 и эффективно ведет себя полиморфно с точки зрения программиста (это ключевое слово компилятора, хотя - реализация неизвестна)

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

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

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

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

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

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

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

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

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

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

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

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

...и в некоторых незначительных отношениях за мой ответ на полиморфизм в C++

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

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

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

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

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

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

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

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

чтобы проиллюстрировать, как шаблоны могут поддерживать отправку (2), а также создание экземпляров (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++ предоставляет возможность создавать экземпляры шаблонов с интегральными параметрами, такими как boolean,int и константы указателя, и используйте их для всех видов вещей без изменения ваших типов данных и, следовательно, без какого-либо полиморфизма вовлеченный. Макросы еще более гибкие.


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


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

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


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

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

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

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

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

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

например:

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

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

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

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

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


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

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

#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
}

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

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

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

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


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

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 возможно, растягивается, концепция определенно существует. Не хватает не способности переопределять функции,а способности специализированных классов автоматически наследовать функции, которые не изменяют поведение.