2014-01-10 4 views
41

C++ 14 позволит создавать шаблоны, которые являются шаблонами. Обычным примером является переменной «пи», который может быть прочитан, чтобы получить значение математической константы Пи для различных типов (3 для int, ближайшее значение можно с float и т.д.)C++ 14 Variable Templates: какова их цель? Любой пример использования?

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

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

+21

Предложение [N3651] (http://isocpp.org/files/papers/N3651.pdf), по-видимому, охватывает много этой информации. –

ответ

19

И кроме примера 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 содержит краткое обоснование.

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

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

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

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

...

Основные проблемы, связанные с "статический член данных" являются:

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

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

...

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

5

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

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

+1

К сожалению, ваш код недействителен. В предложении не делается изменений в синтаксисе. 'Причина в том, что текущая грамматика позволяет параметризовать любое объявление'. Попробуйте на [coliru] (http://coliru.stacked-crooked.com/), используя 'clang ++ --std = C++ 1y'. –

+0

Я не спустил ни к чему. –

+0

@remyabel: Я не понимаю. Я использовал две функции: переменные шаблона и шаблон lambdas. Шаблон lambdas еще не принят. –

18

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

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

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

4

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

1

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

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

+0

Между этой функцией и стенографистами/помощниками типа 'std :: conditional_t' и т. Д. Я часто удивляюсь, почему они пришли так поздно. «[Эмпирические правила] (https://accu.org/content/conf2013/Bjarnes_Talk.pdf)« имеют смысл в принципе, но такие предметы, как «Не добавляйте функции только для того, чтобы следовать моде» и «в состоянии сделать что-то не является достаточным основанием для этого », - уверен звук, похожий на объяснения синтаксиса Flak C++ TMP. Может быть, если бы я знал больше о процессе TR/TS, я бы понял. –

Смежные вопросы