Почему можно изменить постоянную переменную stati c, объявленную внутри функции? - PullRequest
3 голосов
/ 18 января 2020

Я внедряю инфраструктуру ECS для своего игрового движка и исследую способы определения типов компонентов во время выполнения. Это означает, что я могу динамически группировать компоненты подобного типа непрерывно в памяти. Например, у меня может быть два отдельных массива для компонентов Position и Velocity, которые мои системы могут l oop сверх.

В настоящее время я использую typeid(), но я натолкнулся на статью, которая использует static const переменных для генерации уникальных идентификаторов для каждого типа. Общая идея может быть кратко изложена во фрагменте кода ниже:

#include <iostream>

struct Position { float x, y; };
struct Velocity { float dx, dy; };

template <typename T>
int test(int x) {
    static const int y = x;
    return y;
}

int main(int argc, char **argv) {
    std::cout << test<Position>(1) << " ";
    std::cout << test<Position>(2) << " ";
    std::cout << test<Velocity>(3) << " ";
    return 0;
}

Это выводит 1 1 3, но я ожидаю, что, поскольку он пытается изменить константу (особенно при втором вызове), он потерпит неудачу компилировать. Почему это работает?

Ответы [ 2 ]

6 голосов
/ 18 января 2020

Stati c переменные инициализируются только один раз.

Второй (и последующий) момент инициализации пропускается.

Здесь также у вас есть 2 разные функции test. 1 для каждого уникального типа параметра шаблона.

Пример простого вызова функции из вашего кода:

auto f() { return test<Position>(1); }

Это приведет к следующему коду ассемблера, где вы можете видеть, что это проверка, чтобы проверить, была ли переменная уже инициализирована или вернуть уже установленное значение. Если он не был установлен, то он будет настроен как потокобезопасный.

f():
        movzx   eax, BYTE PTR guard variable for int test<Position>(int)::y[rip]
        test    al, al   // <----- check if intialized
        je      .L13     // <----- if not initialized go to .L13
                         // otherwise return the value
        mov     eax, DWORD PTR int test<Position>(int)::y[rip]
        ret
.L13:
        sub     rsp, 8
        mov     edi, OFFSET FLAT:guard variable for int test<Position>(int)::y
        call    __cxa_guard_acquire
        test    eax, eax
        jne     .L14
        mov     eax, DWORD PTR int test<Position>(int)::y[rip]
        add     rsp, 8
        ret
.L14:
        mov     DWORD PTR int test<Position>(int)::y[rip], 1
        mov     edi, OFFSET FLAT:guard variable for int test<Position>(int)::y
        call    __cxa_guard_release
        mov     eax, DWORD PTR int test<Position>(int)::y[rip]
        add     rsp, 8
        ret
2 голосов
/ 18 января 2020

Здесь static const int y = x; y - это const означает, что это non-mutable, а static - продолжительность хранения в программе.

Компилятор выдает ошибку, если вы пытаетесь изменить y непосредственно в последующее утверждение и это неопределенное поведение, если попытаться изменить косвенно.

Из ссылки на c ++,

const object - объект, тип которого является const-qualified, или неизменяемый подобъект объекта const. Такой объект нельзя изменить: попытка сделать это напрямую - ошибка времени компиляции, а попытка сделать это косвенно (например, путем изменения объекта const посредством ссылки или указателя на неконстантный тип) приводит к неопределенному поведению.

...