Что так особенное в использовании goto в цепочке if else

Я смотрел видео Alexandrescu, у него есть следующий фрагмент кода:

 // an example implementation of a single threaded shared_ptr
 ~SingleThreadPtr() { 
    if(!c_) {
       soSueMe: delete p_;
    } else if(--*c_ == 0) {
       delete c_;
       goto soSueMe;
    }
 }

Здесь https://youtu.be/Qq_WaiwzOtI?t=36m44s. Он говорит: "Я использую мою знаменитую конструкцию goto soSueMe" и говорит: "Попробуйте написать это без goto и [..], вы увидите, что это сложно".

Что здесь такого сложного? Не следующее одно и то же, и, очевидно, не сложно и понятно:

 // an example implementation of a single threaded shared_ptr
 ~SingleThreadPtr() { 
    if(!c_) {
       delete p_;
    } else if(--*c_ == 0) {
       delete c_;
       delete p_;
    }
 }

или это не то же самое (тем самым, в первую очередь, усиливая аргументы против goto)? Какого рода хакерская черная магическая вуду происходит здесь?

Ответы

Ответ 1

Дело в том, что деструкторы shared_ptr называются относительно часто и обычно встроенными, и это попытка уменьшить размер встроенного деструктора (поскольку goto намного меньше, чем вызов удаления).

Например, вызов деструктора delete p_ при компиляции может выглядеть примерно так:

LBB5_8:
    movq    -16(%rbp), %rax         ## 8-byte Reload
    movq    (%rax), %rcx
    cmpq    $0, %rcx
    movq    %rcx, -24(%rbp)         ## 8-byte Spill
    je  LBB5_4
    movq    -24(%rbp), %rax         ## 8-byte Reload
    movq    %rax, %rdi
    callq   __ZdlPv                 
LBB5_4:

(где callq __ZdlPv - это деструктор основного объекта, который, наконец, вызывается).

В то время как goto выглядит просто так:

LBB5_8:
    jmp LBB5_2

Таким образом, путем ветвления, а не повторения оператора delete p_, код значительно уменьшается.

Эта сопроводительная презентация может оказаться полезной для чтения (хотя и краткой).

Ответ 2

Не проще ли понять следующий код? Компилятор также разработал еще меньше инструкций по сборке:

~SingleThreadPtr() { 
    if (c_) {
       if (--*c_ != 0) {
            return;
       }
       delete c_;
    }
    delete p_;
}

Инструкции по сборке:

test        rcx,rcx  
je          wmain+0C8h (013F3813D8h)  
dec         dword ptr [rcx]  
jne         wmain+0D5h (013F3813E5h)  
call        qword ptr [__imp_operator delete (013F3831E8h)]  
mov         rcx,qword ptr [p_ (013F385708h)]  
call        qword ptr [__imp_operator delete (013F3831E8h)] 

Исходный код ниже:

~SingleThreadPtr() { 
    if(!c_) {
       soSueMe: delete p_;
    } else if(--*c_ == 0) {
       delete c_;
       goto soSueMe;
    }
 }

Создает следующий код сборки:

test        rcx,rcx  
je          wmain+0A5h (013F3813B5h)  
dec         dword ptr [rcx]  
jne         wmain+0B9h (013F3813C9h)  
call        qword ptr [__imp_operator delete (013F3831E8h)]  
mov         rcx,qword ptr [p_ (013F385708h)]  
call        qword ptr [__imp_operator delete (013F3831E8h)]  
mov         rcx,qword ptr [c_ (013F385710h)]