Великий и ужасный cast

Приведение типов — один из самых неоднозначных инструментов в статически типизированных языках программирования. В самом деле, если для всего есть типы, то зачем конвертировать между ними? Ведь это нарушает безопасный контракт, который, собственно, и достигается использованием системы типов. Но не все так просто.

Во-первых, без приведения типов невозможна низкоуровневая работа с железом (точнее, с драйверами). Если вы хотите передать данные по сети, у вас нет иного выбора, кроме как преобразовать данные в цепочку байт — ничего другого сеть не понимает. Если вы хотите загрузить данные в видеопамять для использования при рендеринге или иных GPU-вычислениях, то это также должен быть простой байтовый буфер — видеодрайвер тоже ничего не знает о системе типов вашего языка.

Во-вторых, тип при вычислении может не совпадать с типом при хранении. Например, пиксели избражений обычно хранятся в памяти как трех- или четырехканальные 8-битные целые. Но это слишком низкая точность информации для вычислений, поэтому функции обработки изображений чаще всего пишутся на float’ах: целочисленное представление пикселя конвертируется в вещественное на входе функции и наоборот — на выходе (именно так, кстати, работает GPU). Аналогично обрабатывается и звук.

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

В D, как и в C++, есть несколько семантик приведения типов. Отличие в том, что все они записываются одинаково — ключевым словом cast. Поэтому очень важно понимать семантику приведения, релизуемую компилятором в различных ситуациях.

(далее…)