diff --git a/main.tex b/main.tex index 8e24d4e..cf48c60 100644 --- a/main.tex +++ b/main.tex @@ -18,6 +18,7 @@ \usepackage{minted} % for highlight +\usepackage{multirow} % for merging rows in tables \voffset=-20mm \textheight=220mm @@ -44,5 +45,5 @@ \include{preprocessor} \include{nullptr} \include{rvalue-references} - +\include{metaprogramming} \end{document} diff --git a/metaprogramming.tex b/metaprogramming.tex new file mode 100644 index 0000000..7c84a1d --- /dev/null +++ b/metaprogramming.tex @@ -0,0 +1,452 @@ +\section{Метапрограммирование в C++11 и выше} +\subsection{SFINAE для выражений} +Одним из самых значительных улучшений SFINAE в C++11 стало появление SFINAE для выражений (Expression SFINAE). \\\\ +Его суть заключается в том, что ошибки подстановки выражений в сигнатуре функции трактуется как ошибки SFINAE, а не как hard-ошибки. +В C++03 гарантировалось лишь то, что ошибки подстановки только в \textit{constant expression'ы} трактуются как ошибки SFINAE. Ошибки подстановки в не-constant expression'ы могли в зависимости от реализации трактоваться и как SFINAE-ошибки, и как hard-ошибки. \\\\ +Часто SFINAE для выражений используется вместе с оператором запятая. В невычисляемом левом аргументе оператора запятая можно перечислить выражения, которые должны участвовать в SFINAE проверках. \\\\ +Ниже приведен пример функции \texttt{void print(T)}, первая перегрузка которой умеет выводить все типы, имеющие функции cbegin() и cend(), вторая --- любые типы, умеющие выводиться в std::cout. +\begin{minted}[linenos, frame=lines, framesep=2mm, tabsize = 4, breaklines]{c++} +template +decltype(std::declval().cbegin(), std::declval().cend(), void()) +print(T container) +{ + std::cout << "Values:{ "; + for(auto value : container) + std::cout << value << " "; + std::cout << "}\n"; +} + +template +decltype(std::cout << std::declval(), void()) +print(T value) +{ + std::cout << "Value: " << value << "\n"; +} +\end{minted} +\subsection{Default template arguments in function templates \& SFINAE} +В С++11 появилась возможность устанавливать значения параметров шаблона функций по умолчанию. \\\\ +Вывод типов в шаблонах функций с аргументами по умолчанию производится следующим образом: +\begin{enumerate} + \item Компилятор пытается вывести тип всех параметров шаблона. + \item Если какой-то параметр вывести не удалось, то используется его значение по умолчанию. Если значение по умолчанию отсутствует --- происходит ошибка вывода. +\end{enumerate} +Небольшой пример для лучшего понимания: +\begin{minted} [linenos, frame=lines, framesep=2mm, tabsize = 4, breaklines]{c++} +template +void f(T t = 0, U u = 0); + +char c; //any symbol + +void g() +{ + f(1, c); // f(1,c) + f(1); // f(1,0) + f(); // error: T cannot be deduced + f(); // f(0,0) + f(); // f(0,0) +} +\end{minted} +Поскольку значение по умолчанию для параметра шаблона может зависеть от других параметров шаблона, для его вычисления делается подстановка, которая может приводить к ошибкам подстановки. В C++11 требуется, чтобы ошибки подстановки в значение по умолчанию параметра шаблона трактовались как ошибки SFINAE. Это нововведение можно использовать для наложения ограничений на типы. \\\\ +Рассмотрим в качестве примера реализацию функции модуля для знаковых чисел: +\begin{minted} [linenos, frame=lines, framesep=2mm, tabsize = 4, breaklines]{c++} +template ::value>>::type> +T abs(T val); +\end{minted} +Второй шаблонный параметр функции \texttt{\textbf{abs}} невозможно вывести из контекста вызова. Поэтому при вызове будет использоваться его значение по умолчанию, содержащее SFINAE-проверку. \\\\ +Достоинством этого метода является то, что SFINAE проверка осуществляется на более ранней стадии компиляции --- при \textit{выводе} типов шаблонных параметров. +Это эффективно в смысле временных затрат на компиляцию, по сравнению с использованием \textbf{\texttt{enable\_if}} в сигнатуре +функции.\\\\ +Но у этого метода есть недостаток. Нельзя перегружать функции, отличающиеся лишь SFINAE-проверками, так как значения по умолчанию не входят в сигнатуру функции. +При попытке так сделать произойдет ошибка компиляции, поскольку две функции имеют одинаковые сигнатуры (redefinition): +\begin{minted} [linenos, frame=lines, framesep=2mm, tabsize = 4, breaklines]{c++} +template ::value>::type> +T abs(T val) +{ + return -val; +} + +template ::value>::type> +T abs(T val) +{ + return val; +} +\end{minted} +\begin{minted}[linenos, frame=lines, framesep=2mm, tabsize = 4, breaklines]{text} +error: redefinition of "template T abs(T)" + T abs(T val) + ^~~ +\end{minted} + +\subsection{Constexpr} +\subsubsection{Constant expressions} +\textbf{\textit{Constant expressions}} --- это такие выражения, значения которых должны быть известны на этапе компиляции. Например, требуется, чтобы размер статического массива и значение non-type шаблонного аргумента были \textit{constant expression}'ами.\\ +Пример: +\begin{minted} [linenos, frame=lines, framesep=2mm, tabsize = 4, breaklines]{c++} +int n = 1; +std::array a1; // error: n is not a constant expression +int arr[n]; // error: n is not a constant expression +const int cn = 2; +std::array a2; // OK: cn is a constant expression +int arr[cn]; // OK: cn is a constant expression +\end{minted} + +Переменные, помеченные как \textbf{const}, не обязательно являются constant expression'ами.\\ Например: +\begin{minted} [linenos, frame=lines, framesep=2mm, tabsize = 4, breaklines]{c++} +int compute_value() +{ + int result; + std::cin >> result; + return result; +} + +int const value = compute_value(); +int arr[value]; // error: value is not a constant expression + +\end{minted} +В данном случае \textbf{const} означает лишь то, что переменную невозможно изменять. Конкретное значение этой переменной известно лишь после запуска программы. \textbf{Const} переменная является \textit{constant expression} только если: +\begin{enumerate} + \item Она является интегральным типом или перечислением. (см. таблицу \ref{tab:types} категорий типов) + \item Выражение, которым инициализируется начальное значение --- \textit{constant expression}. +\end{enumerate} +Результат вызова функции в C++03 никогда не является constant expression'ом: +\begin{minted} [linenos, frame=lines, framesep=2mm, tabsize = 4, breaklines]{c++} +int arr[max(3, 4)]; // error: max(3, 4) is not a constant expression +\end{minted} + +Введенное в C++11 ключевое слово \textbf{constexpr} решает две задачи: +\begin{enumerate} +\item Для переменных --- позволяет указать, что переменная является \textit{constant expression}. +\item Для функций --- позволяет указать, что результат вызова функции является \textit{constant expression}. +\end{enumerate} +\subsubsection{Constexpr переменные} +Переменные, объявленные как \textbf{constexpr}, должны удовлетворять следующим требованиям: +\begin{enumerate} +\item Должны быть проинициализированы сразу при объявлении. +\item Выражение в инициализации должно быть \textit{constant expression}. +\end{enumerate} +Использование \textbf{constexpr} переменной является \textit{constant expression}'ом. \textbf{Constexpr} переменные неявно являются \textbf{const} переменными. \\\\ +Как и \textbf{const} переменные, по умолчанию имеют внутреннюю линковку, в каждой единице трансляции имеют собственный адрес. \\\\ +В C++17 появился способ устанавливать внешнюю линковку для \textbf{constexpr} переменных: +\begin{minted} [linenos, frame=lines, framesep=2mm, tabsize = 4, breaklines]{c++} +//a.cpp +struct X { + static constexpr int x = 42; //declaration +} +\end{minted} +\begin{minted} [linenos, frame=lines, framesep=2mm, tabsize = 4, breaklines]{c++} +//b.cpp +constexpr int X::x; //definition +\end{minted} +Такой код корректен, так как в С++17 все \texttt{static} переменные, являющиеся member'ами какого-то класса, неявно являются inline. +\subsubsection{Constexpr функции} +Объявление функции как \textbf{constexpr} означает, что если все её аргументы являются \textit{constant expression}'ами, то возвращаемое значение может быть посчитано на этапе компиляции. Если значение хотя бы одного аргумента, переданного функции, не известно на момент компиляции, то возвращаемое значение функции вычисляется в run-time, ошибки компиляции не происходит.\\\\ +В C++14 \textbf{constexpr} функции могут содержать в теле следующее: +\begin{enumerate} + \item Любые выражения, кроме \texttt{static} или \texttt{thread\_local} переменных. + \item Конструкции if и switch. + \item Циклы. + \item Выражения, изменяющие значения объектов, если время жизни этих объектов началось в \textbf{constexpr} функции. +\end{enumerate} +Возможна рекурсия. \\\\ +По стандарту \textbf{constexpr} функции неявно являются \texttt{inline}. \\\\ +Пример: +\begin{minted} [linenos, frame=lines, framesep=2mm, tabsize = 4, breaklines]{c++} +constexpr int sum(int n) { + int sum = 0; + for (int i = 1; i <= n; ++i) + { + if (i % 2) + sum += i; + } + return sum; +} + +int main() +{ + int a[sum(10)]; // OK: 10 is a constant expression, so sum(10) - constant expression + int x; + std::cin >> x; + int y = sum(x); + int b[sum(x)]; // compilation error: x is not a constant expression, so sum(x) is not a constant expression + return 0; +} +\end{minted} +В строке 13 в \textbf{constexpr} функцию \texttt{sum} передается \textit{constant expression}, поэтому она считается на этапе компиляции. \\\\ +В строке 16 передается не \textit{constant expression}. Функция просто считается в run-time. \\\\ +Ошибка компиляции в строке 17 подтверждает, что \texttt{sum(x)} не является \textbf{constant expression}. +\subsection{If constexpr} +Пусть требуется написать функцию модуля для всех численных типов. Возможна следующая реализация: +\begin{minted} [linenos, frame=lines, framesep=2mm, tabsize = 4, breaklines]{c++} +template +T abs(T value) +{ + if (is_signed::value) + return (value < 0 ? -value : value); + else + return value; +} +\end{minted} +С помощью обычного if-statement'а и метафункции \texttt{\textbf{is\_signed}} тип проверяется на принадлежность к категории знаковых. В зависимости от этой проверки возвращается либо значение переменной, либо ему противоположное. \\\\ +Условие в if-statement'е являтся \textit{constant expression}'ом, поэтому уже на этапе компиляции известно, какая ветка if-statement'а будет выполняться всегда, а какая является недостижимой. Несмотря на это, код в недостижимой ветке обычного if-statement'а должен быть корректным в любом случае. \\\\ +Это влечет проблемы, если функция \textbf{\texttt{abs}} вызывается от пользовательского числового типа, не имеющего унарного оператора минус (например unsigned\_big\_integer). Подобный вызов приведет к ошибке компиляции. \\\\ +Эту проблему решает конструкция \textbf{if constexpr}, появившаяся в C++17: +\begin{minted} [linenos, frame=lines, framesep=2mm, tabsize = 4, breaklines]{c++} +template +T abs(T value) +{ + if constexpr(is_signed::value) + return (value < 0 ? -value : value); + else + return value; +} +\end{minted} +В отличии от обычного if-statement'a, \textbf{if constexpr} не требует корректности выражений зависимых от шаблонного параметра в false-ветке.\\\\ +Таким образом, \textbf{if constexpr} позволяет создавать compile-time условие. Условие должно являться \textit{constant expression} типа bool.\\\\ +Казалось бы, такое мощное средство позволяет полностью избавиться от необходимости +использования косвенных SFINAE-проверок, использующих шаблоны, и перейти к более удобному синтаксису. Но, в действительности, это не так. +\textbf{If constexpr} не может в полной мере заменить SFINAE. \\\\ +При использовании \textbf{if constexpr} compile-time проверка осуществляется на более поздней стадии --- уже внутри тела функции. В случае если перегрузка от данного типа не имеет смысла и не должна быть сгенерирована вовсе, \textbf{if constexpr} все равно позволит коду функции сгенерироваться.\\\\ +В привиденном выше примере SFINAE-проверка на наличие метода \textbf{\texttt{abs}} от \textbf{\texttt{std::string}} вернет true, что противоречит желаемой действительности. \\\\ +Полностью корректная версия должна содержать SFINAE-проверку: +\begin{minted} [linenos, frame=lines, framesep=2mm, tabsize = 4, breaklines]{c++} +template +enable_if::value, T>::type abs(T value) +{ + return (value < 0 ? -value : value); +} + +template +enable_if::value, T>::type abs(T value) +{ + return value; +} +\end{minted} +\subsection{Static assertions} +Предоставляет возможность писать compile-time asserts. \\\\ +Синтаксис: \\ +\texttt{static\_assert(condition, message)} \\\\ +где \texttt{condition} --- это \textit{constant expression} типа bool, \texttt{message} --- строковый литерал, сообщение выдаваемое при срабатывании ассерта. \\\\ +В случае condition $=$ false, происходит ошибка компиляции, печатается сообщение message, иначе ничего не происходит. \\\\ +Применяется для compile-time проверки свойств, зависящих от параметра шаблона: +\begin{minted} [linenos, frame=lines, framesep=2mm, tabsize = 4, breaklines]{c++} +template +Integral foo(Integral x, Integral y) { + static_assert(std::is_integral::value, "foo() parameter must be an integral type."); +} +\end{minted} +\subsection{Template variables} +В С++14 появилась возможность создавать шаблонные переменные. +\begin{minted} [linenos, frame=lines, framesep=2mm, tabsize = 4, breaklines]{c++} +template +constexpr T pi = T(3.14159265); + +constexpr double x = pi; +constexpr float y = pi; +\end{minted} +Шаблонные переменные обязаны быть \textbf{constexpr}. Допускаются специализации. \\\\ +Наиболее часто применяются для упрощения метафункций из \texttt{std::type\_traits}. \\ +Рассмотрим на примере \texttt{is\_same}: +\begin{minted} [linenos, frame=lines, framesep=2mm, tabsize = 4, breaklines]{c++} +template +constexpr bool is_same_v = is_same::value; +\end{minted} +Теперь вместо \texttt{is\_same::value} можем писать просто \texttt{is\_same\_v}. \\\\ +В C++14 все метафункции, возвращающие значение, имеют соответствующие шаблонные переменные с суффиксом \texttt{\_v}. +\subsection{Type alias \& Alias template} +\textbf{Type alias} --- это псевдоним типа, объявленного ранее(подобно \texttt{typedef}). \textbf{Template alias} --- псевдоним для шаблона.\\\\ +\textbf{Type alias} можно использовать, как альтернативу \texttt{typedef}: \\\\ +\texttt{using mytype = int;} \\\\ +\textbf{Alias template} позволяет написать собственный псевдоним с шаблонным параметром, например: \\ +Синтаксис: +\begin{minted} [frame=lines, framesep=2mm, tabsize = 4, breaklines]{c++} +template +using my_vector = vector>; + +my_vector a; //vector> a; +\end{minted} +Как и \textbf{template variables}, \textbf{alias templates} используются для упрощения \texttt{std::type\_traits}. \\\\ +В C++14 все метафункции, возвращающие тип, имеют соответствующий \textbf{alias template} с суффиксом \texttt{\_t} (\texttt{enable\_if\_t}).\\ +Позволяют сильно упростить код, так как пропадает необходимость писать \texttt{typename} и \texttt{::type}. +\subsection{Явное инстанцирование шаблонов.} +Явное инстанцирование шаблона позволяет сгенерировать код шаблонной функции или класса для конкретных параметров без использования его в коде. \\\\ +Синтаксис: +\begin{minted} [frame=lines, framesep=2mm, tabsize = 4, breaklines]{c++} +template +class my_class {} + +template +V f(T, U) {} + +template class my_class; // for classes +template double f(int, char); // for functions +\end{minted} +При таком инстанцировании инстанцируются все не-template мемберы. +\subsection{Подавление инстанцирования шаблонов.} +В C++11 появилась возможность подавления неявного инстанцирования шаблона. \\\\ +Синтаксис: +\begin{minted} [frame=lines, framesep=2mm, tabsize = 4, breaklines]{c++} +extern template class my_class; // for classes +extern template double f(int, char); // for functions +\end{minted} +Можно использовать это для уменьшения количества генерируемого кода. Например, можно подавить инстанцирование в header-файле и явно проинстанцировать в одном cpp-файле. \\ +Так оптимизируется время компиляции. +\section{std::type\_traits} +\texttt{std::type\_traits} --- это библиотека, которая содержит множество полезных метафункции для работы с классами. В этой части будет проведен её обзор. \\\\ +\subsection{Вспомогательные типы} +\begin{itemize} + \item \texttt{\textbf{integral\_constant} \\ Константа времени компиляции типа T со значением v. \\ + Содержит поле: \\ +\texttt{\textbf{static constexpr T value = v;}} + \item \texttt{\textbf{true\_type}} определен как integral\_constant + \item \texttt{\textbf{false\_type}} определен как integral\_constant +\end{itemize} +Довольно удобно наследоваться от последних двух при написании своих метафункций: +\begin{minted} [frame=lines, framesep=2mm, tabsize = 4, breaklines]{c++} +template +struct is_same : std::false_type {}; + +template +struct is_same : std::true_type {}; +\end{minted} +\subsection{Проверка на принадлежность типа к определенной категории} +\begin{table}[H] + \caption{\label{tab:types} Категории типов} + \begin{center} +\begin{tabular}{|c|c|c|c|c|} + \hline + & \textbf{primary categories} & \multicolumn{3}{|c|}{\textbf{composite categories}} \\ + \hline + \multirow{4}{*}{\textbf{fundamental}} & void & & & \\ + \cline{2-5} + & std::nullptr\_t & & \multirow{7}{*}{scalar} & \multirow{10}{*}{object} \\ + \cline{2-3} + & integral & \multirow{2}{*}{arithmetic} & & \\ + \cline{2-2} + & floating\_point & & & \\ + \cline{1-3} + \multirow{10}{*}{\textbf{compound}}& pointer & & & \\ + \cline{2-3} + & member\_object\_pointer & \multirow{2}{*}{member\_pointer} & & \\ + \cline{2-2} + & member\_function\_pointer & & & \\ + \cline{2-3} + & enum & & & \\ + \cline{2-4} + & union & & & \\ + \cline{2-4} + & class* & & & \\ + \cline{2-4} + & array & & & \\ + \cline{2-5} + & l\_value\_reference & \multirow{2}{*}{reference} & & \\ + \cline{2-2} \cline{4-5} + & r\_value\_reference & & & \\ + \cline{2-5} + & function & & & \\ + \cline{1-5} + \multicolumn{5}{|c|}{* = excluding unions} \\ + \hline +\end{tabular} +\end{center} +\end{table} +К интегральному типу относятся следующе встроенные типы:\\\\ +\texttt{bool, char, char16\_t, char32\_t, wchar\_t, short, int, long, long long}. \\\\ +\textbf{type\_traits} предоставляет набор метафункций, позволяющих проверить, принадлежит ли тип T к какой-либо категории из приведенных в таблице выше. \\\\ +Название таких метафункций имеет вид: \texttt{is\_/*название\_категории*/}. +\subsection{Проверка типа на наличие определенных свойств} +В C++11 поддерживается широкий набор метафункций для проверки наличия у типа определенных свойств. Рассмотрим основные из них: +\begin{itemize} +\item \textbf{\texttt{is\_const}} \\\\ +Проверяет наличие спецификатора const или const volaitile. +\item \textbf{\texttt{is\_volatile}} \\\\ +Проверяет наличие спецификатора volaitile или const volaitile. +\item \textbf{\texttt{is\_signed}} \\\\ +Проверяет, что если тип является арифметическим, то выражение T(-1) < T(0) = true. +То есть тип --- знаковый. +\item \textbf{\texttt{is\_unsigned}} \\\\ +Проверяет, что если тип является арифметическим, то выражение T(0) < T(-1) = true. +То есть тип --- беззнаковый. +\item \texttt{\textbf{is\_trivially\_copyable}} \\\\ +Проверяет, что тип --- \textit{тривиально копируемый}. \\\\ +Тип считается \textit{тривиально копируемым}, если: +\begin{enumerate} +\item Любой его конструктор копирования, move конструктор, копирующий оператор присваивания, +move оператор присваивания является \textit{тривиальным} или помечен как deleted. +\item Хотя бы один из перечисленных не помечен как deleted. +\item Имеет \textit{тривиальный} деструктор, не помеченный как deleted. +\end{enumerate} +Все типы, совместимые с C, тривиально копируемы. \\\\ +Конструктор копирования, move конструктор, копирующий оператор присваивания, +move оператор присваивания (далее --- операция) в классе T называется \textit{тривиальным}, если: +\begin{enumerate} +\item Он не определен пользователем (определен неявно, или помечен как default). +\item T не имеет виртуальных функций. +\item T не имеет виртуальных баз. +\item Соответствующая операция, выбирающаяся для прямой базы T, тривиальна. +\item Соответствующая операция, выбирающаяся для каждого не статического класса-мембера, тривиальна. +\end{enumerate} +Конструктор/деструктор называется \textbf{\textit{тривиальным}}, если: +\begin{enumerate} + \item Он не определен пользователем (определен неявно или помечен как default). + \item Он не виртуальный. + \item Все прямые базы класса имеют тривиальный конструктор/деструктор. + \item Все не статические мемберы класса имеют тривиальный конструктор/деструктор. +\end{enumerate} +За счет наложенных ограничений тривиально копируемый тип может копироваться побайтово. Например, с помощью \texttt{std::memcpy}. +\item \textbf{\texttt{is\_trivial}} \\\\ +Тип считается тривиальным, если он: +\begin{enumerate} +\item Тривиально копируемый. +\item Имеет хотя бы 1 конструктор по умолчанию, все они \textbf{\textit{тривиальны}} или помечены как deleted, +и хотя бы 1 из них не помечен как deleted. +\end{enumerate} +\item \texttt{\textbf{is\_constructible}} $(1)$\\ + \texttt{\textbf{is\_trivially\_constructible}} $(2)$\\ + \texttt{\textbf{is\_nothrow\_constructible}} $(3)$\\\\ +Имеет аргументы: template +\begin{enumerate} +\item Проверяет, что выражение \texttt{T obj(std::declval()}, вызывающее констурктор от типа корректно. +\item Проверяет (1) и что вызов конструктора не вызывает \textit{не тривиальных} операций. +\item Проверяет (1) и что вызов конструктора noexcept. +\end{enumerate} +Есть аналогичные метафункции, проверяющие \texttt{default\_constructible, copy\_constructible, move\_constructible}. Они выражаются через соответствующие версии \texttt{is\_constructible}. +\textbf{\texttt{\item is\_assignable}} $(1)$ \\ +\textbf{\texttt{is\_trivially\_assignable}} $(2)$ \\ +\textbf{\texttt{is\_nothrow\_assignable}} $(3)$ \\\\ +Имеет аргументы: template +\begin{enumerate} +\item Проверяет, что выражение \texttt{std::declval() = std::declval()}, вызывающее оператор присваивания корректно. +\item Проверяет (1) и что вызов конструктора не вызывает \textit{не тривиальных} операций. +\item Проверяет (1) и что вызов конструктора noexcept. +\end{enumerate} +Есть аналогичные метафункции, проверяющие \texttt{copy\_assignable, move\_assignable}. Они выражаются через соответствующие версии \texttt{is\_assignable}. \\\\ +\item Абсолютно аналогично строится тройка метафункций +\texttt{\textbf{is\_destructible, is\_trivially\_destructible, is\_nothrow\_destructible}} +и пара \texttt{\textbf{is\_swapable, is\_nothrow\_swapable}}. +\end{itemize} +\subsection{Взаимоотношения типов} +\begin{itemize} + \item \textbf{\texttt{is\_same}} --- проверяет, что типы T и U совпадают. + \item \textbf{\texttt{is\_base\_of}} --- проверяет, что Derived наследуется от Base или они совпадают. + \item \textbf{\texttt{is\_convertible}} --- проверяет корректность следующего определения функции: + \\\\ \texttt{To test() \{ return std::declval(); \}}\\\\ + что обеспечивает возможность неявного конвертирования From в To. +\end{itemize} +\subsection{Трансформирование типов} +Также в \textbf{type\_traits} определены средства трансформации типов, имеющие интуитивно понятные названия: +\begin{itemize} +\item \texttt{\textbf{remove\_cv}} +\item \texttt{\textbf{remove\_const}} +\item \texttt{\textbf{remove\_volatile}} +\item \texttt{\textbf{add\_cv}} +\item \texttt{\textbf{add\_const}} +\item \texttt{\textbf{add\_volatile}} +\item \texttt{\textbf{remove\_reference}} +\item \texttt{\textbf{add\_lvalue\_reference}} +\item \texttt{\textbf{add\_rvalue\_reference}} +\item \texttt{\textbf{remove\_pointer}} +\item \texttt{\textbf{add\_pointer}} +\end{itemize} \ No newline at end of file