Почему исключение копирования не происходит в этом случае?

Рассмотрим этот код:

#include <iostream>

struct S
{
    S(std::string s) : s_{s} { std::cout << "S( string ) c-tor\n"; }
    S(S const&) { std::cout << "S( S const& ) c-tor\n"; }
    S(S&& s) { std::cout << "S&& c-tor\n"; s_ = std::move(s.s_); }
    S& operator=(S const&) { std::cout << "operator S( const& ) c-tor\n";  return *this;}
    S& operator=(S&& s) { std::cout << "operator (S&&)\n"; s_ = std::move(s.s_); return *this; }
    ~S() { std::cout << "~S() d-tor\n"; }

    std::string s_;
};

S foo() { return S{"blaaaaa"}; }

struct A
{
    A(S s) : s_{s} {}

    S s_;
};

struct B : public A
{
    B(S s) : A(s) {}
};

int main()
{
    B b(foo());
    return 0;
}

Когда я собираю это с g++ -std=c++1z -O3 test.cppЯ получаю следующий вывод:

S( string ) c-tor
S( S const& ) c-tor
S( S const& ) c-tor
~S() d-tor
~S() d-tor
~S() d-tor

Мне интересно, почему нет копии elision? Я ожидаю что-то еще, как это:

S( string ) c-tor
~S() d-tor

Там же вывод, когда я скомпилирую его с -fno-elide-constructors

1 ответ

Решение

Копирование исключений происходит за foo возвращаемое значение, как и ожидалось.

Две другие копии происходят в B а также A Конструкторы. Обратите внимание на вывод, что он вызывает S(S const&) дважды, тогда как можно было бы увидеть хотя бы один S(S&&) за B(foo()), Это потому, что компилятор уже удалил лишние копии, созданные с S(S&&), Если вы компилируете с -fno-elide-constructors Вы можете увидеть эти 2 дополнительные копии:

S::S(std::string)
S::S(S&&)
S::~S()
S::S(S&&)
S::S(const S&)
S::S(const S&)
S::~S()
S::~S()
S::~S()
S::~S()

Тогда как без -fno-elide-constructors выход:

S::S(std::string)
S::S(const S&)
S::S(const S&)
S::~S()
S::~S()
S::~S()

Смотрите copy initialization (инициализация, используемая для аргумента функции):

Во-первых, если T это тип класса и инициализатор prvalue выражение, чей cv-неквалифицированный тип - тот же класс, что и TСамо выражение инициализатора, а не временное материализованное из него, используется для инициализации целевого объекта: см. copy elision.

Вы можете избежать двух оставшихся копий, приняв ссылку:

struct A
{
    A(S&& s) : s_{std::move(s)} {}
    S s_;
};

struct B : public A
{
    B(S&& s) : A(std::move(s)) {}
};

Выход:

S( string ) c-tor <--- foo
S&& c-tor         <--- A::s_
~S() d-tor
~S() d-tor
Другие вопросы по тегам