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

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

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

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

Ответ 1

И кроме примера pi, как бы он работал с неконстантными переменные?

В настоящее время создается экземпляр переменных отдельно для типа. то есть вы можете назначить 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>) является int. decltype((double)n<int>) - двойное и т.д.

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

N3651 дает краткое обоснование.

Увы, существующие правила С++ не позволяют объявление шаблона объявить переменную. Известны обходные пути для этого Проблема:

• использовать constexpr статические элементы данных шаблонов классов

     

• использовать шаблоны функций constexpr, возвращающие нужные значения

Эти обходные решения известны десятилетиями и хорошо документированы. Стандартные классы, такие как std:: numeric_limits, являются архетипическими Примеры. Хотя эти обходные пути не идеальны, их недостатки были в некоторой степени переносимы, потому что в эпоху С++ 03 только простые, встроенные типы констант пользовались неограниченным прямым и эффективным время компиляции. Все это изменилось с принятием constexpr в С++ 11, что расширяет прямые и эффективные поддержка констант пользовательских типов. Теперь программисты делая константы (типов классов) все более очевидными в программах. Так вырастайте путаница и разочарования, связанные с обходные пути.

...

Основные проблемы со "статическим элементом данных":

• им требуются "повторяющиеся" объявления: один раз внутри класса   шаблон, один раз за пределами шаблона класса, чтобы обеспечить "реальный"   в том случае, если константы используются нечасто.

     

• Программисты оба недоумевают и путаются необходимостью предоставления в два раза того же   декларация. Напротив, "обычные" константные объявления не нужны   дублировать декларации.

...

Известные примеры в этой категории, вероятно, являются статическими элементами функции numeric_limits или функции, такие как boost::constants::pi<T>() и т.д. Шаблоны функций Constexpr не страдают от "повторяющихся заявлений" о том, что статические члены данных иметь; кроме того, они обеспечивают функциональную абстракцию. Однако они заставляют программиста заранее выбирать на сайте определения, как константы должны быть доставлены: либо по ссылке const, либо по простой неориентированный тип. Если доставляется константой const, то константы должны систематически выделяться в статическом хранилище; если не ссылочным типом, то константы нуждаются в копировании. Копирование не является проблема для встроенных типов, но это showstopper для пользовательских типы со смысловой семантикой, которые arent просто обертки вокруг крошечных встроенные типы (например, матрица или целое число, или bigfloat и т.д.). контраст, "обычные" константы (expr) не страдают от этого проблема. Предоставлено простое определение, и решение нужны ли константы для компоновки только в хранилище зависит от использования, а не от определения.

Ответ 2

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

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

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

Ответ 3

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

Ответ 4

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

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

Ответ 5

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

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

которые люди пишут для выполнения метапрограммирования шаблонов перед вводом шаблонов переменных. Для значений, отличных от типа, мы могли это сделать с С++ 11 с помощью constexpr, поэтому переменные шаблона имеют только преимущество, позволяющее вычислять на основе типов переменные шаблоны.

TL; DR: Они не позволяют нам делать то, что мы не могли сделать раньше, но они делают метапрограммирование шаблона менее PITA.

Ответ 6

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

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>);
    ...
}