книги хакеры / журнал хакер / 193_Optimized
.pdf
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
|
|
|
|
|
|
hang |
e |
|
|
|
|
|
||
|
|
|
C |
E |
|
|
|
|
|
|
|
|
|
|
C |
|
E |
|
|
|
|||||||
|
|
X |
|
|
|
|
|
|
|
|
|
|
|
|
X |
|
|
|
|
|
|
||||||
|
- |
|
|
|
|
|
d |
|
|
|
|
|
|
- |
|
|
|
|
|
d |
|
||||||
|
F |
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
F |
|
|
|
|
|
|
|
i |
|
||
|
|
|
|
|
|
|
|
t |
|
|
|
|
|
|
|
|
|
|
|
|
t |
|
|||||
P |
D |
|
|
|
|
|
|
|
|
o |
|
|
|
P |
D |
|
|
|
|
|
|
|
|
o |
|||
|
|
|
NOW! |
r |
|
|
|
|
|
|
|
NOW! |
r |
||||||||||||||
|
|
|
|
BUY |
|
|
|
Академия С++ |
|
|
|
|
|
BUY |
|
|
|||||||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
||||
|
|
|
|
to 110 |
|
|
|
|
|
|
|
|
|
|
|
|
to |
|
|
|
|
|
|
|
|||
w |
|
|
|
|
|
|
|
|
|
|
m |
|
|
|
w |
|
|
|
|
|
|
|
|
|
m |
||
w Click |
|
|
|
|
|
|
|
o |
|
|
|
w Click |
|
|
|
|
|
|
o |
||||||||
|
w |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
w |
|
|
|
|
|
|
|
|
|
||
|
. |
|
|
|
|
|
|
|
.c |
|
|
|
|
|
. |
|
|
|
|
|
|
.c |
|
||||
|
|
p |
df |
|
|
|
|
e |
|
|
|
|
|
|
|
p |
df |
|
|
|
|
e |
|
||||
|
|
|
|
g |
|
|
|
|
|
|
|
|
|
|
|
g |
|
|
|
||||||||
|
|
|
|
n |
|
|
|
|
|
|
|
|
|
|
|
|
|
n |
|
|
|
|
|||||
|
|
|
|
-xcha |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-x cha |
|
|
|
|
|
|||
|
|
|
|
РАЗРЫВНЕСУЩЕСТВУЮЩЕГОШАБЛОНА |
ДАПОМОЖЕТНАМSTD:: |
|
|
|
|
|
|
|
|
|
|
|
|||||||||||
|
|
|
|
Давай сделаем наш пример чуть более веселым — |
С каждым годом в стандартную библиотеку добавляется все больше инструментов для ме- |
|
|
|
|
|
|
||||||||||||||||
|
|
|
|
уберем общий шаблон поведения для Some и func, |
тапрограммирования. Как правило, все новое — это хорошо опробованное старое, поза- |
|
|
|
|
|
|
||||||||||||||||
|
|
|
|
оставив лишь уже написанные специализации |
имствованное из библиотеки Boost.MPL и узаконенное. Нам все чаще требуется #include |
|
|
|
|
|
|
||||||||||||||||
|
|
|
|
Some<int> и func<double> и, конечно же, не трогая |
<type_traits>, и все больше кода идет с применением развилок вида std::enable_if, |
|
|
|
|
|
|
||||||||||||||||
|
|
|
|
предварительное объявление. |
|
|
все больше нам требуется знать на этапе компиляции, не является ли аргумент шаблона |
|
|
|
|
|
|
||||||||||||||
|
|
|
|
|
|
Что в этом случае произойдет с шаблоном |
целочисленным типом std::is_integral, или, например, сравнить два типа внутри ша- |
|
|
|
|
|
|
||||||||||||||
|
|
|
|
create<T>? Он просто перестанет компилиро- |
блона с помощью std::is_same, чтобы управлять поведением специализаций шаблона. |
|
|
|
|
|
|
||||||||||||||||
|
|
|
|
ваться для любого типа. Ведь для |
create<int> |
Вспомогательные структуры шаблона выстроены так, что компилируется только |
|
|
|
|
|
|
|||||||||||||||
|
|
|
|
не |
|
|
|
существует |
реализации |
функции |
func<int>, |
та специализация, что дает истинность выражения, а специализации для ложного пове- |
|
|
|
|
|
|
|||||||||
|
|
|
|
а для create<double> нет нужного Some<double>. Пер- |
дения отсутствуют. |
|
|
|
|
|
|
|
|
|
|
|
|||||||||||
|
|
|
|
вая же попытка вставить в код вызов create для како- |
Чтобы стало более понятно, рассмотрим подробнее std::enable_if. Этот шаблон за- |
|
|
|
|
|
|
||||||||||||||||
|
|
|
|
го-либо типа приведет к ошибке компиляции. |
висит от истинности первого своего аргумента (второй опционален), и выражение вида |
|
|
|
|
|
|
||||||||||||||||
|
|
|
|
|
|
Чтобы оставить возможность работать функ- |
std::enable_if<predicate>::type будет скомпилировано лишь для истинных выраже- |
|
|
|
|
|
|
||||||||||||||
|
|
|
|
ции |
|
|
|
create<T>, |
нужно специализировать Some<T> |
ний, делается это довольно просто — специализаци- |
|
|
|
|
|
|
|
|
|
|
|
||||||
|
|
|
|
и func<T> хотя бы от одного типа одновременно. |
ей от значения true: |
|
|
|
|
|
|
|
|
|
|
|
|||||||||||
|
|
|
|
Можно реализовать Some<double> или func<int>, на- |
template <bool predicate_value, |
|
|
|
|
|
|
|
|
|
|
|
|||||||||||
|
|
|
|
пример так: |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|||||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class result_type = void> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
template <> |
|
|
|
struct enable_if; |
|
|
|
|
|
|
|
|
|
|
|
||||||||
|
|
|
|
int func(int const& value) |
|
|
template<class result_type> |
|
|
|
|
|
|
|
|
|
|
|
|||||||||
|
|
|
|
{ |
|
|
|
|
|
|
|
|
|
|
|
struct enable_if<true, result_type> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return value; |
|
|
{ |
|
|
|
|
|
|
|
|
|
|
|
||||
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
typedef result_type type; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
template <> |
|
|
|
}; |
|
|
|
|
|
|
|
|
|
|
|
||||||||
|
|
|
|
class Some<double> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|||||||||
|
|
|
|
{ |
|
|
|
|
|
|
|
|
|
|
|
Для значения false типа std::enable_if<P, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
public: |
|
|
|
T>::type компилятор просто не сможет создать спе- |
|
|
|
|
|
|
|
|
|
|
|
||||||||
|
|
|
|
|
|
|
|
|
explicit Some(double value) |
|
циализацию шаблона, и это можно использовать, на- |
|
|
|
|
|
|
|
|
|
|
|
|||||
|
|
|
|
|
|
|
|
|
} |
|
: m_value(value*value) { |
|
пример ограничив поведение ряда типов частичной |
|
|
|
|
|
|
|
|
|
|
|
|||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
специализации шаблонной структуры или класса. |
|
|
|
|
|
|
|
|
|
|
|
||
|
|
|
|
|
|
|
|
|
double get_value() const { |
|
Здесь в помощь в качестве аргументов |
|
|
|
|
|
|
|
|
|
|
|
|||||
|
|
|
|
|
|
|
|
|
} |
|
return m_square; |
|
|
std::enable_if могут быть использованы самые |
|
|
|
|
|
|
|
|
|
|
|
||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
разнообразные структуры-предикаты из того же |
|
|
|
|
|
|
|
|
|
|
|
||
|
|
|
|
private: |
|
|
|
<type_traits>: std::is_signed<T>::value истинно, |
|
|
|
|
|
|
|
|
|
|
|
||||||||
|
|
|
|
}; |
|
|
|
|
double m_square; |
|
|
если тип T поддерживает тип знак + или - (что очень |
|
|
|
|
|
|
|
|
|
|
|
||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
удобно для отсечения поведения беззнаковых целых), |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
std::is_loating_point<T>::value истинно для ве- |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Добавив две специализации, мы не только ожи- |
щественных типов float и double, std::is_same<T1, |
|
|
|
|
|
|
|
|
|
|
|
|||||||||
|
|
|
|
вили компиляцию специализаций create от типов |
T2>::value истинно, если типы T1 и T2 совпадают. |
|
|
|
|
|
|
|
|
|
|
|
|||||||||||
|
|
|
|
int и double, получилось еще и так, что возвращать |
Структур предикатов, помогающих нам, множе- |
|
|
|
|
|
|
|
|
|
|
|
|||||||||||
|
|
|
|
для этих типов алгоритм будет одни и те же значения. |
ство, а если чего не хватает в std:: или boost::, мож- |
|
|
|
|
|
|
|
|
|
|
|
|||||||||||
|
|
|
|
Но поведение при этом будет разным! |
|
но запросто сделать свою структуру. |
|
|
|
|
|
|
|
|
|
|
|
||||||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Что ж, вводная часть завершена, переходим |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
к практике. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
INFO |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
В C++ типы ведут себя по-разному и не всегда шаблонный алгоритм |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
ведет себя эффективно для всех типов. Зачастую, добавив специали- |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
зацию шаблона, мы получаем не только прирост производительности, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
но и более понятное поведение программы в целом. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
hang |
e |
|
|
|
|
|
|||
|
|
|
C |
|
E |
|
|
|
|
|
|
|
|
|
|
|
|
|
C |
|
E |
|
|
|
|||||||
|
|
X |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
X |
|
|
|
|
|
|
||||||
|
- |
|
|
|
|
|
|
d |
|
|
|
|
|
|
|
|
|
- |
|
|
|
|
|
d |
|
||||||
|
F |
|
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
|
F |
|
|
|
|
|
|
|
i |
|
||
|
|
|
|
|
|
|
|
|
t |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
t |
|
|||||
P |
D |
|
|
|
|
|
|
|
|
|
o |
|
|
|
|
|
|
P |
D |
|
|
|
|
|
|
|
|
o |
|||
|
|
|
|
NOW! |
r |
|
|
|
|
|
|
|
|
|
NOW! |
r |
|||||||||||||||
|
|
|
|
|
BUY |
|
|
|
|
|
|
|
|
|
|
|
|
|
BUY |
|
|
||||||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Откровения метапрограммиста |
|
|
|
|
|
|
|
|
|
|
|
|||
|
|
|
|
to |
ХАКЕР 02 /193/ 2015 |
|
|
|
|
|
|
to |
|
|
|
|
|
|
|
||||||||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
111 |
|
|
|
|
|
|
|||
w |
|
|
|
|
|
|
|
|
|
|
|
m |
|
|
|
|
|
|
w |
|
|
|
|
|
|
|
|
|
m |
||
w Click |
|
|
|
|
|
|
|
|
o |
|
|
|
|
|
|
w Click |
|
|
|
|
|
|
o |
||||||||
|
w |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
w |
|
|
|
|
|
|
|
|
|
||
|
. |
|
|
|
|
|
|
|
|
.c |
|
|
|
|
|
|
|
|
. |
|
|
|
|
|
|
.c |
|
||||
|
|
p |
df |
|
|
|
|
|
e |
|
|
|
|
|
|
|
|
|
|
p |
df |
|
|
|
|
e |
|
||||
|
|
|
|
|
|
g |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
g |
|
|
|
|||||||
|
|
|
|
|
n |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
n |
|
|
|
|
|||||
|
|
|
|
-xcha |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
-x cha |
|
|
|
|
|
||||
|
|
|
|
|
Попробуем-ка создать страшную функцию, возвращающую разный результат |
|
|
|
|
|
|
|
|
|
|
|
|||||||||||||||
|
|
|
|
|
для одинаковых и разных типов аргументов шаблона. В этом нам поможет |
|
|
|
|
|
|
|
|
|
|
|
|||||||||||||||
|
|
|
|
|
механизм частичной специализации для вспомогательной структуры |
|
|
|
|
|
|
|
|
|
|
|
|||||||||||||||
|
|
|
|
|
ВХОДПОШАБЛОНУСТРОГОВОСПРЕЩЕН |
|
template <class result_type, |
|
|
|
|
|
|
|
|
|
|
|
|||||||||||||
|
|
|
|
|
Чтобы начать программировать программный код и заняться всяческим метапрограмми- |
class value_type> |
|
|
|
|
|
|
|
|
|
|
|
||||||||||||||
|
|
|
|
|
рованием, попробуем-ка создать страшную функцию, возвращающую разный результат |
struct type_cast |
|
|
|
|
|
|
|
|
|
|
|
||||||||||||||
|
|
|
|
|
для одинаковых и разных типов аргументов шаблона. В этом нам поможет механизм ча- |
{ |
|
|
|
|
|
|
|
|
|
|
|
||||||||||||||
|
|
|
|
|
стичной специализации для вспомогательной структуры. |
static bool try_cast(result_type&, |
|
|
|
|
|
|
|
|
|
|
|
||||||||||||||
|
|
|
|
|
|
|
Поскольку частичной специализации для функций не существует, внутри функции мы |
value_type const&) |
|
|
|
|
|
|
|||||||||||||||||
|
|
|
|
|
будем просто обращаться к простой соответствующей специализации структуры, у кото- |
{ |
|
|
|
|
|
|
|
|
|
|
|
||||||||||||||
|
|
|
|
|
рой мы и зададим частичную специализацию: |
|
static_assert(false, |
|
|
|
|
|
|
|
|
|
|
|
|||||||||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
"Здесь нужно понятное |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
template <class result_type, |
|
сообщение об ошибке"); |
|
|
|
|
|
|
|
|
|
|
|
|||||||||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class value_type> |
|
} |
|
|
|
|
|
|
|
|
|
|
|
|||
|
|
|
|
|
struct type_cast; |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|||||||||||
|
|
|
|
|
template <class result_type, |
|
|
|
|
|
|
|
|
|
|
|
|
|
|||||||||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
class value_type> |
|
Таким образом, каждый раз, когда функция try_ |
|
|
|
|
|
|
||||||||
|
|
|
|
|
bool try_safe_cast(result_type& result, |
|
safe_cast пытается привести типы, для которых нет |
|
|
|
|
|
|
||||||||||||||||||
|
|
|
|
|
{ |
|
|
|
|
|
|
|
|
|
|
|
value_type const& value) |
соответствующей специализации структуры type_ |
|
|
|
|
|
|
|||||||
|
|
|
|
|
|
|
|
|
return type_cast<result_type, |
|
cast, будет выдаваться сообщение об ошибке компи- |
|
|
|
|
|
|
||||||||||||||
|
|
|
|
|
|
|
|
|
|
|
ляции из общего шаблона. |
|
|
|
|
|
|
|
|
|
|
|
|||||||||
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
value_type>::try_cast(result, value); |
Заготовка готова, пора приступать к метапрограм- |
|
|
|
|
|
|
||||||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
мированию! |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
template <class same_type> |
|
|
|
|
|
|
|
|
|
|
|
|
|
|||||||||||||
|
|
|
|
|
struct type_cast<same_type, same_type> |
|
ПОМЕТАПРОГРАММИРУЙМНЕТУТ! |
|
|
|
|
|
|
|
|
|
|
|
|||||||||||||
|
|
|
|
|
{ |
|
|
|
|
static bool try_cast(result_type& result, |
Для начала нужно поправить объявление вспомо- |
|
|
|
|
|
|
||||||||||||||
|
|
|
|
|
|
|
|
|
|
гательной структуры type_cast. Нам потребуется |
|
|
|
|
|
|
|||||||||||||||
|
|
|
|
|
|
|
|
|
|
{ |
|
|
|
|
|
|
value_type const& value) |
дополнительный тип meta_type для логической раз- |
|
|
|
|
|
|
|||||||
|
|
|
|
|
|
|
|
|
|
|
|
result = value; |
|
вилки без ущерба для передаваемых параметров |
|
|
|
|
|
|
|||||||||||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
и неявного определения их типов. Теперь описание |
|
|
|
|
|
|
||||||||||
|
|
|
|
|
|
|
|
|
|
} |
|
|
return true; |
|
|
шаблона структуры будет выглядеть чуть сложнее: |
|
|
|
|
|
|
|
|
|||||||
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Очевидно, что мы создали |
|
|
|
|
|
|
|
|
|
|
|
|
|
|||||||||||
|
|
|
|
|
заготовку для функции без- |
|
|
|
|
|
|
|
|
|
|
|
|
|
|||||||||||||
|
|
|
|
|
опасного |
приведения |
типов. |
|
|
|
|
|
|
|
|
|
|
|
|
|
|||||||||||
|
|
|
|
|
Функция основывается на типах |
КАКУСТРОЕНЫПРЕДИКАТЫ |
|
|
|
|
|
|
|
|
|
|
|
|
|||||||||||||
|
|
|
|
|
переданных в нее аргументов |
|
|
|
|
|
|
|
|
|
|
|
|
||||||||||||||
|
|
|
|
|
и идет выполнять статический |
Предикат — это обычная частичная специализация шаблонной структу- |
|
|
|
|
|
|
|
|
|
|
|
||||||||||||||
|
|
|
|
|
метод try_cast у соответству- |
ры. Например, для std::is_same в общем случае все выглядит примерно |
|
|
|
|
|
|
|
|
|
|
|
||||||||||||||
|
|
|
|
|
ющей |
|
|
специализации |
струк- |
так: |
|
|
|
|
|
|
|
|
|
|
|
|
|||||||||
|
|
|
|
|
туры type_cast. В настоящий |
template <class T1, class T2> |
|
|
|
|
|
|
|
|
|
|
|
|
|||||||||||||
|
|
|
|
|
момент мы реализовали только |
|
|
|
|
|
|
|
|
|
|
|
|
||||||||||||||
|
|
|
|
|
тривиальный случай, когда тип |
struct is_same; |
|
|
|
|
|
|
|
|
|
|
|
|
|||||||||||||
|
|
|
|
|
значения |
совпадает с |
типом |
template <class T> |
|
|
|
|
|
|
|
|
|
|
|
|
|||||||||||
|
|
|
|
|
результата |
и преобразование, |
struct is_same<T,T> |
|
|
|
|
|
|
|
|
|
|
|
|
||||||||||||
|
|
|
|
|
по сути, не нужно. Переменной |
{ |
|
|
|
|
|
|
|
|
|
|
|
|
|||||||||||||
|
|
|
|
|
результата просто присваивает- |
static const bool value = true; |
|
|
|
|
|
|
|
|
|
|
|
|
|||||||||||||
|
|
|
|
|
ся входящее значение, и всегда |
}; |
|
|
|
|
|
|
|
|
|
|
|
|
|||||||||||||
|
|
|
|
|
возвращается true — признак |
template <class T1, class T2> |
|
|
|
|
|
|
|
|
|
|
|
|
|||||||||||||
|
|
|
|
|
успешного |
приведения |
типа |
struct is_same |
|
|
|
|
|
|
|
|
|
|
|
|
|||||||||||
|
|
|
|
|
значения к типу результата. |
{ |
|
|
|
|
|
|
|
|
|
|
|
|
|||||||||||||
|
|
|
|
|
|
|
Для |
|
несовпадающих |
типов |
static const bool value = false; |
|
|
|
|
|
|
|
|
|
|
|
|
||||||||
|
|
|
|
|
сейчас |
|
будет |
выдана ошибка |
}; |
|
|
|
|
|
|
|
|
|
|
|
|
||||||||||
|
|
|
|
|
компиляции с длинным не- |
|
|
|
|
|
|
|
|
|
|
|
|
|
|||||||||||||
|
|
|
|
|
понятным текстом. Чтобы не- |
Для совпадающих типов аргументов std::is_same компилятор C++ |
|
|
|
|
|
|
|
|
|
|
|
||||||||||||||
|
|
|
|
|
много |
|
|
поправить |
это |
дело, |
выберет подходящую специализацию, в данном |
случае частичную |
|
|
|
|
|
|
|
|
|
|
|
||||||||
|
|
|
|
|
необходимо ввести общую ре- |
с value = true, а для несовпадающих попадет в общую реализацию ша- |
|
|
|
|
|
|
|
|
|
|
|
||||||||||||||
|
|
|
|
|
ализацию шаблона со static_ |
блона с value = false. Компилятор всегда пытается отыскать строго под- |
|
|
|
|
|
|
|
|
|
|
|
||||||||||||||
|
|
|
|
|
assert(false, |
…) в теле ме- |
ходящую специализацию по типам аргументов и, лишь не найдя нужную, |
|
|
|
|
|
|
|
|
|
|
|
|||||||||||||
|
|
|
|
|
тода try_cast — это сделает |
идет в общую реализацию шаблона. |
|
|
|
|
|
|
|
|
|
|
|
|
|||||||||||||
|
|
|
|
|
сообщение об |
ошибке |
более |
|
|
|
|
|
|
|
|
|
|
|
|
|
|||||||||||
|
|
|
|
|
понятным: |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
|
|
C |
|
E |
|
|
|
|||
|
|
X |
|
|
|
|
|
|
|||
|
- |
|
|
|
|
|
d |
|
|
||
|
F |
|
|
|
|
|
|
t |
|
|
|
|
D |
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
|
|
r |
|
||
P |
|
|
|
|
|
NOW! |
o |
|
|||
|
|
|
|
|
|
|
|
||||
|
|
|
|
|
BUY |
|
|
Академия С++ |
|||
|
|
|
|
|
|
|
|
|
|
||
w Click |
to 112 |
|
|
m |
|||||||
w |
|
|
|
|
|
|
|
|
|
|
|
|
w |
|
|
|
|
|
|
|
o |
|
|
|
. |
|
|
|
|
|
.c |
|
|
||
|
|
p |
|
|
|
|
g |
|
|
|
|
|
|
|
df |
|
|
n |
e |
|
|
||
|
|
|
|
-xcha |
|
|
|
|
|
template <class result_type, class value_type,
class meta_type = void> struct type_cast;
Как видно, новый тип в объявлении шаблона опционален и никак не мешает уже существующим объявлениям специализации и общего поведения шаблона. Однако этот маленький нюанс позволяет нам управлять успешностью компиляции, передавая третьим параметром результат std::enable_if<предикат>::type. Специализации с некомпилируемым параметром шаблона будут отброшены, что нам и нужно, чтобы управлять логикой приведения типов различных групп.
Ведь очевидно, что целые числа приводятся друг к другу по-разному, в зависимости от того, есть ли у обоих типов знак, какой тип большей разрядности и не выходит ли переданное значение value за пределы допустимых значений для result_type.
Так, если оба типа — знаковые целые и тип результата большей разрядности, нежели тип входящего значения, то можно без проблем присвоить результату входящее значение, это же верно и для беззнаковых типов. Давай опишем это поведение специальной частичной специализацией шаблона type_cast:
template <class result_type, class value_type>
struct type_cast<result_type, value_type,
typename std::enable_if<...>::value>
{
static bool try_cast(result_type& result,
value_type const& value) {
result = value;
return true;
}
};
Теперь нужно разобраться, что за условие нам нужно вставить вместо многоточия параметром std::enable_if.
Поехали описывать условие времени компиляции:
typename std::enable_if<
Во-первых, специализация не должна пересекаться с уже существующей, где тип результата и входящего значения совпадают:
!std::is_same<result_type, value_type>::value &&
Во-вторых, мы рассматриваем случай, когда оба аргумента шаблона — целочисленные типы:
std::is_integral<result_type>::value && std::is_integral<value_type>::value &&
В-третьих, мы подразумеваем, что оба типа либо знаковые, либо беззнаковые (скобки обязательны — условия параметров шаблона вычисляются иначе, нежели на этапе выполнения!):
(std::is_signed<result_type>::value == std::is_signed<value_type>::value) &&
В-четвертых, разрядность целочисленного типа результата больше, чем разрядность типа переданного значения (снова обязательны скобки!):
(sizeof(result_type) > sizeof(value_type))
Если оба типа — знаковые целые и тип результата большей разрядности, нежели тип входящего значения, то можно без проблем
присвоить результату входящее значение, это же верно и для беззнаковых типов
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
|
C |
|
E |
|
|
|||
|
|
X |
|
|
|
|
|
|||
|
- |
|
|
|
|
|
d |
|
||
|
F |
|
|
|
|
|
|
t |
|
|
|
D |
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
|
r |
||
P |
|
|
|
|
|
NOW! |
o |
|||
|
|
|
|
|
|
|
||||
|
|
|
|
|
BUY |
|
|
|||
|
|
|
|
|
|
|
|
|
|
|
ХАКЕР 02 /193/ 2015 |
|
|
|
|
|
|||||
w Click |
to |
|
|
|
|
|
m |
|||
|
|
|
|
|
|
|||||
w |
|
|
|
|
|
|
|
|
|
|
|
w |
|
|
|
|
|
|
|
o |
|
|
. |
|
|
|
|
|
.c |
|
||
|
|
p |
|
|
|
|
g |
|
|
|
|
|
|
df |
|
|
n |
e |
|
||
|
|
|
|
-x cha |
|
|
|
|
И наконец, закрываем объявление std::enable_if:
>::type
В результате type для std::enable_if будет сгенерирован только при выполнении указанных четырех условий. В остальных случаях для прочих комбинаций типов данная частичная специализация даже не будет создана.
Получается зубодробительное выражение внутри std::enable_if, которое отсекает исключительно указанный нами случай. Данный шаблон спасает от тиражирования кода приведения различных целочисленных типов друг в друга.
Чтобы закрепить материал, можно описать чуть более сложный случай — приведение беззнакового целого к типу меньшей разрядности беззнакового целого. Тут нам поможет знание бинарного представления целого числа и стандартный класс std::numeric_limits:
template <typename result_type, typename
value_type>
struct type_cast<result_type, value_type,
typename
std::enable_if<...>::type>
{
static bool try_cast(result_type& result,
value_type const& value)
{
if (value != (value &
std::numeric_limits
<result_type>::max()))
{
return false;
}
result = result_
type(value);
return true;
}
};
В условии if все достаточно просто: максимальное значение типа result_type неявно приводится к типу большей разрядности value_type и вы-
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
|
C |
|
E |
|
|
|||
|
|
X |
|
|
|
|
|
|||
|
- |
|
|
|
|
|
d |
|
||
|
F |
|
|
|
|
|
|
t |
|
|
|
D |
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
|
r |
||
P |
|
|
|
|
NOW! |
o |
||||
|
|
|
|
|
|
|||||
|
|
|
|
|
|
|
||||
|
|
|
|
|
BUY |
|
|
|
|
|
w Click |
to |
ХАКЕР 02 /193/ 2015 |
||||||||
|
|
|
|
|
m |
|||||
|
|
|
|
|
|
|||||
w |
|
|
|
|
|
|
|
|
|
|
|
w |
|
|
|
|
|
|
|
o |
|
|
. |
|
|
|
|
|
.c |
|
||
|
|
p |
|
|
|
|
g |
|
|
|
|
|
|
df |
|
|
n |
e |
|
||
|
|
|
|
-xcha |
|
|
|
|
Откровения метапрограммиста
|
|
|
|
hang |
e |
|
|
|
|
|
|
|
|
C |
|
E |
|
|
|||
|
|
X |
|
|
|
|
|
|||
|
- |
|
|
|
|
|
d |
|
||
|
F |
|
|
|
|
|
|
t |
|
|
|
D |
|
|
|
|
|
|
|
i |
|
|
|
|
|
|
|
|
|
r |
||
P |
|
|
|
|
|
NOW! |
o |
|||
|
|
|
|
|
|
|
||||
|
|
|
|
|
BUY |
|
|
|||
|
|
|
|
|
|
|
|
|
|
|
|
|
|
113 |
|
|
|
|
|
||
w Click |
to |
|
|
|
|
|
m |
|||
w |
|
|
|
|
|
|
|
|
|
|
|
w |
|
|
|
|
|
|
|
o |
|
|
. |
|
|
|
|
|
.c |
|
||
|
|
p |
|
|
|
|
g |
|
|
|
|
|
|
df |
|
|
n |
e |
|
||
|
|
|
|
-x cha |
|
|
|
|
ступает в качестве маски для значения value. В случае если для значения value задействованы биты вне result_type, мы получим выполненное неравенство и попадем на return false.
Теперь пройдем по условию времени компиляции:
typename std::enable_if<
Первые два условия остаются теми же — оба типа целочисленные, но различаются между собой:
!std::is_same<result_type, value_type>::value &&
std::is_integral<result_type>::value &&
std::is_integral<value_type>::value &&
Оба типа являются беззнаковыми целыми:
std::is_unsigned<result_type>::value &&
std::is_unsigned<value_type>::value &&
Тип результата меньшей разрядности, нежели тип входящего значения (скобки обязательны!):
(sizeof(result_type) < sizeof(value_type))
Все условия перечислены, закрываем условие специализации:
>::type
Для знаковых целых, где результат меньшей разрядности, условие будет похожим, но с двумя std::is_signed внутри std::enable_if, однако условие выхода за пределы значений будет несколько другим:
static bool try_cast(result_type& result, value_type const& value)
{
if (value != (value &
(std::numeric_limits<result_type>::max() |
std::numeric_limits<value_type>::min())))
{
return false;
}
result = result_type(value);
return true;
}
Снова вспоминаем бинарное представление целых чисел со знаком: здесь маской будет бит знака входящего значения и биты значения типа результата, исключая бит знака. Соответственно, минимальное число типа value_type, где заполнен только бит знака, объединенное побитово с максимальным числом типа result_type, где заполнены все биты, кроме знакового, и будет давать нам искомую маску допустимых значений.
В качестве домашнего задания рассмотри следующие случаи:
1.Приведение знакового к беззнаковому с использованием уже написанных специализаций и модификатора std::make_unsigned.
2.Приведение беззнакового к знаковому большей разрядности с использованием уже написанных специализаций и модификатора std::make_signed.
3.Чуть посложнее: приведение беззнакового к знаковому меньшей или равной разрядности с использованием условия невыхода за пределы значений и модификатора std::make_signed.
Также не составит труда написать аналогичные специализации для преобра-
зования из std::is_loating_point типов, а также преобразование из типа bool. Для полного удовлетворения можно дописать приведение из строковых типов и обратно и оформить это столь нужной всем библиотекой безопасного приведения типов C++.