为什么在这种情况下不会出现复制省略?

bla*_*zio 3 c++ copy-elision

考虑以下代码:

#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;
}
Run Code Online (Sandbox Code Playgroud)

当我编译它时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
Run Code Online (Sandbox Code Playgroud)

我想知道为什么没有副本省略?我期待更像这样的东西:

S( string ) c-tor
~S() d-tor
Run Code Online (Sandbox Code Playgroud)

使用-fno-elide-constructors编译它时,输出相同

Max*_*kin 5

foo正如预期的那样,复制省略确实会发生返回值.

另外两个副本发生在BA构造函数中.在它调用输出通知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()
Run Code Online (Sandbox Code Playgroud)

而没有-fno-elide-constructors输出是:

S::S(std::string)
S::S(const S&)
S::S(const S&)
S::~S()
S::~S()
S::~S()
Run Code Online (Sandbox Code Playgroud)

请参阅复制初始化(用于函数参数的初始化):

首先,如果T是类类型且初始化程序是prvaluecv-unqualified类型与其相同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)) {}
};
Run Code Online (Sandbox Code Playgroud)

输出:

S( string ) c-tor <--- foo
S&& c-tor         <--- A::s_
~S() d-tor
~S() d-tor
Run Code Online (Sandbox Code Playgroud)