Позволяет ли const (теоретическая) оптимизация здесь? - PullRequest
10 голосов
/ 12 марта 2019

Рассмотрим этот фрагмент:

void foo(const int&);

int bar();

int test1()
{
    int x = bar();
    int y = x;
    foo(x);
    return x - y;
}

int test2()
{
    const int x = bar();
    const int y = x;
    foo(x);
    return x - y;
}

В моем понимании стандарта ни x, ни y не могут быть изменены на foo в test2, тогда как они могут быть измененына foo в test1 (например, с помощью const_cast для удаления const из const int&, потому что ссылочные объекты на самом деле не являются константными в test1).

Теперь, ни gcc, ни clang, ни MSVC , похоже, не оптимизируют test2 до foo(bar()); return 0;, и я могу понять, что они не хотят тратить впустую оптимизационные пассы на оптимизацию, которая редко применяется на практике.

Но я, по крайней мере, прав в своем понимании этой ситуации, или я упускаю какой-то законный способ изменения x в test2?

Ответы [ 2 ]

6 голосов
/ 12 марта 2019

Стандарт гласит: [dcl.type.cv] :

За исключением того, что любой член класса, объявленный mutable […], может быть изменен, любая попытка изменить[…] Константный объект […] во время его жизни […] приводит к неопределенному поведению.

Также невозможно сделать это определенным, преждевременно завершив жизнь объекта, согласно [basic.life] :

Создание нового объекта в хранилище, которое занимает полный объект const с […] автоматическим хранением, или в хранилище, в котором находится такой объект constиспользуется до истечения срока службы, что приводит к неопределенному поведению.

Это означает, что оптимизация x - y до нуля действительна, потому что любая попытка изменить x в foo приведет кнеопределенное поведение.

Интересный вопрос, есть ли причина не выполнять эту оптимизацию в существующих компиляторах.Учитывая, что определение объекта const является локальным по отношению к test2 и факт используется в той же функции, обычные исключения, такие как поддержка вставки символов, здесь не применяются.

0 голосов
/ 13 марта 2019

const очень мало помогает или практически ничего не оптимизирует.Компилятору в основном требуется глобальное представление кода, чтобы решить, являются ли переменные действительно постоянными или нет, и компилятор определит это независимо от модификаторов const.Рассмотрим этот код (например, в godbolt.org)

void foo(const int& v) { const_cast<int&>(v) = 6; }
const int bar() { return 9; }
int main() {
     const int a = bar();
     const int b = a;
     foo(a);
     return a-b;
}

, который приведет к -O3 в gcc8.3 и clang7 к очень правильному и оптимальному (даже с дополнительным несущественным const_cast, который вызываетнеопределенное поведение):

foo(int const&):                              # @foo(int const&)
        mov     dword ptr [rdi], 6
        ret
bar():                                # @bar()
        mov     eax, 9
        ret
main:                                   # @main
        mov     eax, -3
        ret

и что важно: это идентично, если вы замените все const int просто int.Таким образом, компилятору помогает , а не , это глобальный просмотр и анализ кода.

В заключение позвольте мне процитировать эту очень интересную страницу GotW от Херба Саттера http://www.gotw.ca/gotw/081.htm, которая, как я вижу, также начала весь выпуск.

Таким образом, const остается особенностью преимущественно для программистов.Компиляторы умнее нас и не доверяют нам в любом случае; -)

И последнее замечание: почти во всех случаях оптимизация является функцией компилятора, а не языка.Есть некоторые исключения из этого, например copy elision, но вопрос этого вопроса не относится к их числу.

...