Шаблоны переменных в C++14

232
01 ноября 2017, 03:30

Изучая новый стандарт C++, натолкнулся на нововведение «шаблоны переменных»

Синтаксис у шаблона следующий:

template < typename T >
constexpr T value = T(1234);

О применении шаблона написано:

Данная возможность позволяет создавать и использовать constexpr шаблоны переменных, для более удобного сочетания с шаблонными алгоритмами.

Не вполне понимаю, как такая переменная будет использоваться в шаблонном алгоритме. Не могли бы Вы привести несколько примеров использования этого механизма? Также не понимаю, почему нельзя такое шаблонное constexpr выражение заменить нешаблонным, ведь значение мы указываем явно, соответственно, и тип можем написать (вывести) явно.

Answer 1

В стандарте есть пример:

template<class T>
constexpr T pi = T(3.1415926535897932385L);
template<class T>
T circular_area(T r) {
  return pi<T> * r * r;
}

Здесь шаблон переменной позволяет получить константу нужного размера - float/double/etc.

Другое популярное применение - это замена is_some<T>::value на is_some_v<T>, например

template< class T, class U >
inline constexpr bool is_same_v = is_same<T, U>::value;  // C++17
Answer 2

Тут можно заметить, что при объявлении шаблона переменной тип этой переменной совсем не обязан совпадать с параметром шаблона или вообще быть как-то связан с параметром(-ами) шаблона. Большинство тривиальных примеров шаблонов переменных обычно дают переменной тот же самый тип, что использовался в параметре шаблона, что может создать ложное впечатление, как будто это требуется. На самом деле шаблонная переменная может иметь любой тип.

Например, пользуясь примером с площадью окружности, вы можете принять решение использовать "базовое" определение константы PI с конкретным типом double

template<class T> constexpr double PI = 3.141592653589793;

а затем, пользуясь явной специализацией, добавить специализированные определения для отдельных плавающих типов

template<> constexpr float PI<float> = 3.1415927;
template<> constexpr long double PI<long double> = 3.141592653589793238L;

После этого можно будет реализовать "единую" шаблонную функцию вычисления площади окружности

template<class T> T circle_area(T r) 
{
  return PI<T> * r * r;
}

которая будет, например, работать и с целочисленными типами

int area = circle_area(10);

Для целочисленных типов в качестве константы PI будет браться вариант "по умолчанию" с типом double, а для плавающих типов будут использоваться специализированные значения этой константы.

Вариант реализации этой функции из ответа @Abyx, как вы наверное заметили, тоже будет "работать" с целочисленными типами. Но в том варианте константа Пи для целочисленных типов получит целочисленное значение 3, что не всегда желательно.

Также можно добавить, что возможность включения статических членов-данных в шаблоны классов существовала в языке С++ с самого начала стандартизованных времен, т.е. в С++98. Это позволяло реализовывать "шаблоны переменных" уже тогда, пользуясь фактически той же самой техникой, которая в С++98 применялась для "шаблонных typedef", т.е. через помещение типов или переменных в "обертку" шаблонного класса. В частности, вышеприведенный пример может быть реализован на классическом С++98 как

template <typename T> struct PI { static const double value; };
template <typename T> const double PI<T>::value = 3.141592653589793;
template <> struct PI<float> { static const float value; };
const float PI<float>::value = 3.1415927;
template <> struct PI<long double> { static const long double value; };
const long double PI<long double>::value = 3.141592653589793238L;
template<class T> T circle_area(T r) 
{
  return PI<T>::value * r * r;
}

Принимая это во внимание, можно сказать, что шаблонные переменные не являются каким-то фундаментально новым свойством С++14. Они (выражаясь нестрого) являются просто "синтаксическим сахаром", более компактным и элегантным вариантом синтаксиса для записи вышеприведенного С++98 варианта реализации. Ситуация аналогична введенной в С++11 возможность объявления шаблонных typedef (через using), которая тоже может быть названа "синтаксическим сахаром" над хорошо известной С++98 техникой "шаблонного typedef".

READ ALSO
класс property в c++ [требует правки]

класс property в c++ [требует правки]

для чего используеться класс property в c++?

215
С чего начать изучение GUI C++ [требует правки]

С чего начать изучение GUI C++ [требует правки]

Всем приветДовольно много учил язык c++ и работать в консольке уже поднадоело и хочется начать реализовывать небольшие проекты (работать...

246
иерархия интерфейсов в с++ [требует правки]

иерархия интерфейсов в с++ [требует правки]

есть ли в с++ иерархия интерфейсов?

306
Зейдель надоел уже, помогите с методом

Зейдель надоел уже, помогите с методом

Собственно код, помогите довести до ума, входит в бесконечный циклЕсли у кого есть другой рабочий код, то киньте его ниже

307