Инициализация ссылочного члена в классе С++ 11 делает неверную копию

Вот моя ситуация:

  • У меня есть класс с переменной-членом ссылки const.
  • Я пытаюсь инициализировать эту переменную-член из константной ссылки.

Моя проблема заключается в том, что когда переменная-член инициализируется в классе, создается временная копия рассматриваемого объекта, и для ссылки используется временный адрес. Когда переменная-член инициализируется в списке инициализаторов конструктора, копия не создается. В любом случае программа отлично компилируется без предупреждений в g++ 4.8.3 с флагами c++11 или c++1y.

Ниже приведена минимальная программа и вывод. Я просто хотел бы лучше понять правила этого, чтобы знать, почему это происходит (или если это ошибка).

#include <iostream>
using namespace std;
struct A
{
    A( )
    {
        cout << "creating an A at " << this << endl;
    }

    A( const A & a )
    {
        cout << "copying an A from instance at " << & a << " to instance at " << this << endl;
    }
};

A g_aardvark;

const A & GetAardvark( )
{
    cout << "returning an A at " << & g_aardvark << endl;
    return g_aardvark;
}

struct B
{
    B( )
        : m_a1( GetAardvark( ) )
        , m_a2( g_aardvark )
    { }
    const A & m_a1;
    const A & m_a2;
    const A & m_a3{ GetAardvark( ) };
    const A & m_a4{ g_aardvark };
};

int main( )
{
    B butter;
    cout << "B has m_a1 at " << & butter.m_a1 << endl;
    cout << "B has m_a2 at " << & butter.m_a2 << endl;
    cout << "B has m_a3 at " << & butter.m_a3 << endl;
    cout << "B has m_a4 at " << & butter.m_a4 << endl;
    return 0;
}

Пример вывода:

creating an A at 0x601494
returning an A at 0x601494
returning an A at 0x601494
copying an A from instance at 0x601494 to instance at 0x7fffc595f87f
copying an A from instance at 0x601494 to instance at 0x7fffc595f87e
B has m_a1 at 0x601494
B has m_a2 at 0x601494
B has m_a3 at 0x7fffc595f87f
B has m_a4 at 0x7fffc595f87e

person Scott    schedule 08.09.2016    source источник
comment
Если мне не изменяет память, это была ошибка в инициализации списка ссылок gcc, исправленная в версии 4.9. Вы должны иметь возможность воспроизводить гораздо более простой код, например. int main() { A a; A const &b { a } ; }   -  person M.M    schedule 08.09.2016
comment
Какую версию компилятора вы используете? В этом коде нет копий: coliru.stacked-crooked.com/a/f30df5ef62b45420   -  person Peregring-lk    schedule 08.09.2016


Ответы (2)


Это случай печально известного DR 1288. В опубликованном стандарте C++11 был дефект, из-за которого непреднамеренно указывалось, что код:

const A & m_a4{ g_aardvark };

на самом деле создаст временный файл из g_aardvark и привяжется к нему. Это конечно бред, но g++ 4.8 следовал опубликованному тексту. Стандарт был исправлен DR 1288, так что ссылка связывается напрямую.

Clang всегда реализовывал разумное поведение, но g++ не обновлялся до цепочки 4.9.

См. здесь пояснения со стандартными ссылками и более простым тестовым примером.

person M.M    schedule 08.09.2016

По-видимому, это ошибка компилятора, как описано выше в Peregring-lk.

http://coliru.stacked-crooked.com/a/f30df5ef62b45420

person Scott    schedule 08.09.2016