C++11: чья константность константнее
2014-01-10 04:55 pmgcc4.8.3_amd64 -std=c++11 -O3
Сначала небольшой безобидный кусочек кода в стиле C++03:
==исходный код 1==
uint32_t f( uint32_t x )
{
static const uint32_t local_array[] = { 0x11112222U, 0x33334444U, 0x55556666U, 0x77778888U };
return local_array[x & 3U];
}====
==машинный код 1==
.text:0000000000000000 ; f(unsigned int) .text:0000000000000000 public _Z1fj .text:0000000000000000 _Z1fj proc near .text:0000000000000000 and edi, 3 .text:0000000000000003 mov eax, dword ptr ds:_ZZ1fjE11local_array[rdi*4] ; f(uint)::local_array .text:000000000000000A .text:000000000000000A locret_A: .text:000000000000000A retn .text:000000000000000A _Z1fj endp .rodata:0000000000000170 ; f(unsigned int)::local_array .rodata:0000000000000170 _ZZ1fjE11local_array dd 11112222h .rodata:0000000000000174 dd 33334444h .rodata:0000000000000178 dd 55556666h .rodata:000000000000017C dd 77778888h
==
Всё чинно, константная табличка в .rodata и обращение к ней из секции .text
Делаем ещё более константный массив, в стиле C++11-константнее-некуда:
==исходный код 2==
uint32_t f( uint32_t x )
{
constexpr uint32_t local_array[] = { 0x11112222U, 0x33334444U, 0x55556666U, 0x77778888U };
return local_array[x & 3U];
}====
В ответ это компилятор выдаёт ужасы нашего городка:
==машинный код 2==
.text:0000000000000000 ; =============== S U B R O U T I N E ======================================= .text:0000000000000000 .text:0000000000000000 .text:0000000000000000 ; f(unsigned int) .text:0000000000000000 public _Z1fj .text:0000000000000000 _Z1fj proc near .text:0000000000000000 .text:0000000000000000 var_18 = dword ptr -18h .text:0000000000000000 var_14 = dword ptr -14h .text:0000000000000000 var_10 = dword ptr -10h .text:0000000000000000 var_C = dword ptr -0Ch .text:0000000000000000 .text:0000000000000000 and edi, 3 .text:0000000000000003 mov [rsp+var_18], 11112222h .text:000000000000000B mov [rsp+var_14], 33334444h .text:0000000000000013 mov [rsp+var_10], 55556666h .text:000000000000001B mov [rsp+var_C], 77778888h .text:0000000000000023 mov eax, [rsp+rdi*4+var_18] .text:0000000000000027 .text:0000000000000027 locret_27: .text:0000000000000027 retn .text:0000000000000027 _Z1fj endp
====
Красный террор и холокост в одном флаконе: "констатная" таблица перевычисляется при каждом вызове функции, да ещё и размещается в зарезервированной области стека для параметров, которую должна создавать вызывающая сторона по конвенции ABI.
Какой конпелятор, такое и O3. Поддержка C++11 на уровне "пришей собаке хвост".
no subject
Date: 2014-01-10 04:10 pm (UTC)Это замена всяким извращениям вида "посчитать Фибоначчи на шаблонах".
constexpr = посчитать на этапе компиляции. что компилятор и делает.
no subject
Date: 2014-01-10 05:13 pm (UTC)однако constexpr-константы это немного другое.
предположим, есть embedded-код, который в header-файле объявляет кучу констант
1) сишники использовали #define:
#define MY_COOL_CONSTANT 0x12345
2) c++ники (секта имени 2003 года) используют static const
static const uint32_t MY_COOL_CONSTANT = 0x12345;
при этом глюпый кросс-компилятор дветысячибородатого года с патчами под embedded-железку, но без последних оптимизационных фишечек тупо сваливает все эти static const в секцию данных, и оперативная память на AVRке (например) заканчивается, потому что её там всего лишь 2048 байт
3) на претензии из пункта "2" лучшие собаководы из секты имени 2003 года советуют юзать enum:
enum { MY_COOL_CONSTANT = 0x12345 };
в принципе неплохо, но теряется тип и не поддерживаются например константы типа double
так что часть народа не впечатлилась и продолжили использовать #define
"непорядок" - решил Страуструп и предложил новую фенечку - constexpr
типа как static const только ещё константнее, максимально близко к #define
всё это достаточно подробно разжёвывается в "10.4 Constant Expressions" книги "Язык программирования C++, 4-е издание" за авторством Страуструпа, суть сводится к тому что constexpr-выражение - это всегда литерал, и если его нельзя вычислить в контексте компилятора, то имеем ошибку.
однако gcc всё равно решил, что надо вычислять на каждом входе в функцию
ну OK
no subject
Date: 2014-01-10 05:18 pm (UTC)no subject
Date: 2014-01-10 05:39 pm (UTC)int f( uint32_t x ) { static const int local_array[] = { rand(), rand(), rand(), rand() }; return local_array[x & 3]; }Он компилируются. Код, который генерируется компилятором, вызывает rand() четыре раза при первом входе в функцию, а затем сохраняет результаты в глобальной переменной. При последующих вызовах используются старые значения.
Всё просто: static в этом контексте означает "вычислить не более одного раза",
а const означает "элементы массива нельзя изменять после того, как они были проинициализированы"
И ни то, ни другое не означает "вычислить массив на этапе компиляции".
Для того, чтобы выразить требование "вычислить массив на этапе компиляции" в C++11 есть ключевое слово constexpr, ну а в C++03 для выражения этого требования нет нужных ключевых слов, строго говоря.
no subject
Date: 2014-01-11 07:05 am (UTC)no subject
Date: 2014-01-11 07:06 am (UTC)за такую кодогенерацию надо просто отрывать голову.