C ++ 14 позволит создавать переменные, которые являются шаблонами. Обычным примером является переменная ‘pi’, которую можно прочитать, чтобы получить значение математической константы π для различных типов (3 для int
; ближайшее значение возможно с float
, так далее.)
Кроме того, что мы можем получить эту функцию, просто заключив переменную в шаблонную структуру или класс, как это сочетается с преобразованиями типов? Я вижу некоторые совпадения.
И кроме примера с пи, как он будет работать с неконстантными переменными? Любой пример использования, чтобы понять, как максимально использовать такую функцию и какова ее цель?
И кроме примера пи, как это будет работать с неконстантным
переменные?
В настоящее время создается впечатление, что переменные создаются отдельно для типа. то есть, вы можете назначить 10 для n<int>
и это будет отличаться от определения шаблона.
template<typename T>
T n = T(5);
int main()
{
n<int> = 10;
std::cout << n<int> << " "; // 10
std::cout << n<double> << " "; // 5
}
Если декларация const
Это только для чтения. Если это constexpr
, как все constexpr
декларации, он не имеет большого применения снаружи constexpr
(ressions).
Кроме того, мы можем получить эту функцию, просто обернув переменную
в шаблонной структуре или классе, как это смешивается с типом
преобразования?
Это должно быть простое предложение. Я не вижу, как это существенно влияет на преобразования типов. Как я уже говорил, тип переменной — это тип, с которым вы создали экземпляр шаблона. т.е. decltype(n<int>)
это инт. decltype((double)n<int>)
двойной и так далее.
Любой пример использования, чтобы понять, как максимально использовать такую функцию
и какова его цель?
N3651 дает краткое обоснование.
Увы, существующие правила C ++ не позволяют объявлению шаблона
объявить переменную. Есть хорошо известные обходные пути для этого
проблема:• использовать статические данные constexpr членов шаблонов классов
• использовать шаблоны функций constexpr, возвращающие нужные значения
Эти обходные пути известны на протяжении десятилетий и хорошо документированы.
Стандартные классы, такие как std :: numeric_limits, являются типичными
Примеры. Хотя эти обходные пути не идеальны, их недостатки
были терпимы в некоторой степени, потому что в эпоху C ++ 03 только простые,
встроенные типы констант пользовались беспрепятственным прямым и эффективным
поддержка времени компиляции. Все это изменилось с принятием
Переменные constexpr в C ++ 11, которые расширяют прямые и эффективные
поддержка констант пользовательских типов. Теперь программисты
делая константы (типов классов) все более очевидными в программах.
Так растут путаница и разочарования, связанные с
обходные пути.
…
Основные проблемы со «статическим членом данных»:
• они требуют «дублирующих» объявлений: один раз внутри класса
шаблон, находящийся вне шаблона класса для предоставления «реального» определения в случае использования констант odr.• программисты недовольны и смущены необходимостью предоставлять дважды одинаковые
декларация. Напротив, «обычные» константные объявления не нужны
дубликаты деклараций.
…
Хорошо известными примерами в этой категории являются, вероятно, статический член
функции numeric_limits или такие функции, как
boost::constants::pi<T>()
и т.д. шаблоны функций Constexpr не
страдать от проблемы «дубликатов объявлений», что статические члены данных
иметь; кроме того, они обеспечивают функциональную абстракцию. Однако они
вынудить программиста заранее выбрать, на сайте определения, как
константы должны быть доставлены: либо ссылкой на константу, либо
простой не ссылочный тип. Если доставлено по константной ссылке, то
константы должны систематически размещаться в статическом хранилище; если
по нереферентному типу, тогда константы нужно копировать. Копирование не
проблема для встроенных типов, но это showtopper для определенных пользователем
типы со значениями семантики, которые не просто обертывают вокруг
встроенные типы (например, матрица, или целое число, или bigfloat, и т. д.)
напротив, «обычные» const (expr) переменные не страдают от этого
проблема. Дается простое определение и решение
действительно ли константы должны быть размещены только в хранилище
зависит от использования, а не от определения.
мы можем получить эту функцию, просто обернув переменную в шаблонную структуру или класс
Да, но это будет бесполезная синтаксическая соль. Не здоров для кровяного давления.
pi<double>
передает намерение лучше, чем pi<double>::value
, Коротко и точно. Этого достаточно в моей книге, чтобы разрешить и поддержать этот синтаксис.
Интересно, возможно ли что-то в этом направлении: (при условии наличия лямбда-шаблонов)
void some_func() {
template<typename T>
std::map<int, T> storage;
auto store = []<typename T>(int key, const T& value) { storage<T>[key] = value; };
store(0, 2);
store(1, "Hello"s);
store(2, 0.7);
// All three values are stored in a different map, according to their type.
}
Теперь это полезно?
Как более простое использование, обратите внимание, что инициализация pi<T>
использует явное преобразование (явный вызов унарного конструктора) и неравномерную инициализацию. Что означает, что, учитывая тип radians
с конструктором radians(double)
, ты можешь написать pi<radians>
,
Другой практический пример шаблонов переменных C ++ 14 — когда вам нужна функция для передачи чего-либо в std::accumulate
:
template<typename T>
T const & (*maxer) (T const &, T const &) = std::max<T>;
std::accumulate(some.begin(), some.end(), initial, maxer<float>);
Обратите внимание, что с помощью std::max<T>
недостаточно, потому что не может вывести точную подпись. В этом конкретном примере вы можете использовать max_element
вместо этого, но дело в том, что существует целый класс функций, которые разделяют это поведение.
Ну, вы можете использовать это, чтобы написать код времени компиляции следующим образом:
#include <iostream>
template <int N> const int ctSquare = N*N;
int main() {
std::cout << ctSquare<7> << std::endl;
}
Это значительное улучшение по сравнению с аналогом
#include <iostream>
template <int N> struct ctSquare {
static const int value = N*N;
};
int main() {
std::cout << ctSquare<7>::value << std::endl;
}
что люди писали для выполнения метапрограммирования шаблонов до того, как были введены переменные шаблоны. Для не типовых значений мы смогли сделать это, так как C ++ 11 с constexpr
Таким образом, переменные шаблона имеют только то преимущество, что допускают вычисления на основе типов для шаблонов переменных.
TL; DR: они не позволяют нам делать то, что мы не могли делать раньше, но они делают шаблонное метапрограммирование менее PITA.