выражение в typeid оценивается дважды во время выполнения?

Обратите внимание, что выражение в операторе typeid будет оцениваться во время выполнения, если оно является lvalue типа с виртуальным членом.

У меня есть тривиальный класс Base следующим образом

class Base
{
public:
    Base(const std::string &s):sval(s){}
    virtual ~Base()=default; 
private:
    std::string sval;
};

и тривиальная функция для возврата lvalue из Base следующим образом:

Base& ChangeBase(Base &b)
{
    std::cout<<"Called"<<std::endl;
    return b;
}

Когда я написал следующие коды для проверки оператора typeid:

int main()
{

    Base b("Dream");
    typeid (ChangeBase(b));
    return 0;
}

Я получил следующий вывод:

Called
Called

Это указывало на то, что функция ChangeBase вызывалась дважды, значит ли это, что выражение в typeid будет оцениваться дважды во время выполнения (если необходимо оценить во время выполнения)? Если да, то почему?

Я использую gcc 4.9.3


person Tim_King    schedule 24.11.2016    source источник
comment
К вашему сведению, clang 3.8 печатает Called один раз.   -  person WhozCraig    schedule 24.11.2016
comment
Gcc6.1.0 тоже печатает один раз.   -  person songyuanyao    schedule 24.11.2016
comment
Не воспроизводится с помощью g++ (coliru.stacked-crooked.com/a/34e4906ceb99affa)   -  person Cheers and hth. - Alf    schedule 24.11.2016
comment
gcc 4.8.3 печатает один раз.   -  person Sergey    schedule 24.11.2016
comment
@skypjack Если передается выражение glvalue полиморфного типа, указанное выражение оценивается во время выполнения. ОП в этом прав.   -  person sigy    schedule 24.11.2016
comment
@songyuanyao Я использую gcc 4.9.3   -  person Tim_King    schedule 24.11.2016
comment
@sigy Верно. Я только что видел фрагменты в вопросе. Неверно прочитал определение Base и неправильно понял намерение ОП. Моя вина.   -  person skypjack    schedule 24.11.2016


Ответы (1)


GCC сначала проверяет значение на нулевое значение, затем снова оценивает его и только после этого вызывает typeid.

Сборка: https://godbolt.org/g/SoLFYq

    lea     rax, [rbp-64]
    mov     rdi, rax
    call    ChangeBase(Base&)
    test    rax, rax
    je      .L8
    lea     rax, [rbp-64]
    mov     rdi, rax
    call    ChangeBase(Base&)
...
.L8:
    call    __cxa_bad_typeid
person Zefick    schedule 24.11.2016
comment
Спасибо. Любое понимание того, почему этого поведения нет в gcc 4.8.3 и 6.1? - person Tim_King; 24.11.2016