Шаблоны переменных C ++ 14: какова их цель? Любой пример использования?

C ++ 14 позволяет создавать шаблонные переменные. Обычный пример - это переменная pi, которую можно прочитать, чтобы получить значение математической константы π для различных типов (3 для int; ближайшее возможное значение с float и т. Д.)

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

И, кроме примера с Пи, как он будет работать с неконстантными переменными? Любой пример использования, чтобы понять, как максимально использовать такую ​​функцию и какова ее цель?


person jbgs    schedule 10.01.2014    source источник
comment
Предложение N3651, похоже, охватывает большую часть этой информации.   -  person Shafik Yaghmour    schedule 10.01.2014


Ответы (6)


И, кроме примера с Пи, как он будет работать с неконстантными переменными?

В настоящее время создается впечатление, что переменные создаются отдельно для типа. т.е. вы можете присвоить 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 (рессий).

Кроме того, мы можем получить эту функцию, просто заключив переменную в шаблонную структуру или класс, как это сочетается с преобразованием типов?

Это должно быть простое предложение. Я не могу понять, как это существенно влияет на преобразование типов. Как я уже сказал, тип переменной - это тип, с которым вы создали экземпляр шаблона. т.е. 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 не страдают от проблемы «повторяющихся объявлений», которая есть у статических элементов данных; кроме того, они обеспечивают функциональную абстракцию. Однако они заставляют программиста заранее выбрать на сайте определения, как константы должны быть доставлены: либо с помощью ссылки на константу, либо с помощью простого не ссылочного типа. Если доставляется по ссылке на константу, то константы должны систематически размещаться в статической памяти; если по не ссылочному типу, то необходимо копировать константы. Копирование не является проблемой для встроенных типов, но это демонстрация возможностей для определяемых пользователем типов с семантикой значений, которая не является просто оболочкой для крошечных встроенных типов (например, матричных, целочисленных, bigfloat и т. Д.) Напротив, " обычные "const (expr) переменные" не страдают от этой проблемы. Предоставляется простое определение, и решение о том, действительно ли нужно размещать константы в хранилище, зависит только от использования, а не от определения.

person Community    schedule 16.01.2014

мы можем получить эту функцию, просто заключив переменную в шаблонную структуру или класс.

Да, но это было бы беспричинной синтаксической солью. Не полезно для артериального давления.

pi<double> передает намерение лучше, чем pi<double>::value. Коротко и по делу. В моей книге этого достаточно, чтобы разрешить и поощрять такой синтаксис.

person n. 1.8e9-where's-my-share m.    schedule 16.01.2014

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

person Levi Morrison    schedule 05.08.2015

Интересно, возможно ли что-то в этом роде: (при условии наличия лямбда-выражений шаблона)

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>.

person Laurent LA RIZZA    schedule 16.01.2014
comment
К сожалению, ваш код недействителен. Предложение не вносит никаких изменений в синтаксис. The reason is that the current grammar allows any declaration to be parameterized. Попробуйте это на coliru, используя clang++ --std=c++1y. - person ; 16.01.2014
comment
@remyabel: Я не понимаю. Я использовал две функции: переменные шаблона и лямбда-выражения шаблона. Лямбды шаблонов пока не принимаются. - person Laurent LA RIZZA; 16.01.2014
comment
О, общие лямбды. Он по-прежнему не компилируется, но я попытался приблизиться к нему: coliru.stacked- crooked.com/a/54ab174f073762c2 - person ; 17.01.2014
comment
Я исправил лямбда-код в своем посте. Я использовал [key] = value вместо insert. - person Laurent LA RIZZA; 17.01.2014
comment
Crud! Это не работает. Я не могу зафиксировать storage в лямбде, потому что это не переменная. Это шаблон переменной ... - person Laurent LA RIZZA; 17.01.2014
comment
У меня есть пример компиляции в вопросе, который я разместил здесь (прокрутите вниз для ссылки на живой пример), однако он требует явных экземпляров для каждой пары, которую вы будете использовать, и каждая карта - это отдельная переменная. - person ; 17.01.2014
comment
Верно. Это как бы противоречит цели. Единственное оставшееся решение для моей идеи - это шаблон глобальной переменной с бесплатным шаблоном функции. - person Laurent LA RIZZA; 17.01.2014
comment
Запрет на использование так называемых «локальных шаблонов» в C ++ является давним, даже несмотря на появление полиморфных лямбда-выражений. - person Luc Danton; 20.01.2014
comment
Вы уже можете сделать что-то подобное, используя ключевое слово using: template<typename T> using store = map<std::string, T>; - person RamblingMad; 09.04.2014
comment
@CoffeeandCode: это создает только новое имя шаблонного типа, но не объявляет одну переменную для каждого аргумента типа. - person Laurent LA RIZZA; 10.04.2014
comment
Есть средства для его компиляции, но есть ошибки ссылки (coliru.stacked-crooked.com / a / 5db647f2bf85420f). storage не может быть локальным, и теперь существуют общие лямбды. - person akim; 22.11.2016

Что ж, вы можете использовать это для написания кода времени компиляции следующим образом:

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

person cmaster - reinstate monica    schedule 19.09.2017
comment
Между этой функцией и сокращениями / помощниками, такими как std::conditional_t и т. Д. Я часто задаюсь вопросом, почему они появились так поздно. эмпирические правила в принципе имеют смысл, но такие пункты, как Не добавляйте функции просто следовать моде и «способность что-то делать - еще не достаточная причина для этого», безусловно, звучит как объяснение синтаксиса flak C ++ TMP. Может быть, если бы я знал больше о процессе TR / TS, я бы понял. - person John P; 28.10.2017

У меня здесь есть пример использования.

template<typename CT> constexpr CT MARK = '%';
template<> constexpr wchar_t MARK<wchar_t> = L'%';

которые используются в шаблоне обработки строк.

template <typename CT> 
void ProcessString(const std::basic_string<CT>& str)
{
    auto&& markpos = str.find(MARK<CT>);
    ...
}
person Tiger Hwang    schedule 19.10.2019