为什么在这种情况下不会发生复制省略?
Why does copy elision not occur in this case?
考虑这段代码:
#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
我想知道为什么没有复制省略?我期待更多这样的事情:
S( string ) c-tor
~S() d-tor
用-fno-elide-constructors编译时有相同的输出
正如预期的那样,foo
return 值确实发生了复制省略。
另外两个副本发生在 B
和 A
构造函数中。请注意,在输出中它调用了两次 S(S const&)
,而对于 B(foo())
,人们会期望看到至少一个 S(S&&)
。这是因为编译器已经消除了使用 S(S&&)
创建的那些额外副本。如果你用 -fno-elide-constructors
编译,你可以看到这两个额外的副本:
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(用于函数参数的初始化):
First, if T
is a class type and the initializer is a prvalue
expression whose cv-unqualified type is the same class as T
, the initializer expression itself, rather than a temporary materialized from it, is used to initialize the destination object: see 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
考虑这段代码:
#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
我想知道为什么没有复制省略?我期待更多这样的事情:
S( string ) c-tor
~S() d-tor
用-fno-elide-constructors编译时有相同的输出
正如预期的那样,foo
return 值确实发生了复制省略。
另外两个副本发生在 B
和 A
构造函数中。请注意,在输出中它调用了两次 S(S const&)
,而对于 B(foo())
,人们会期望看到至少一个 S(S&&)
。这是因为编译器已经消除了使用 S(S&&)
创建的那些额外副本。如果你用 -fno-elide-constructors
编译,你可以看到这两个额外的副本:
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(用于函数参数的初始化):
First, if
T
is a class type and the initializer is aprvalue
expression whose cv-unqualified type is the same class asT
, the initializer expression itself, rather than a temporary materialized from it, is used to initialize the destination object: see 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