C++ 为什么在这种情况下不发生复制省略?

C++ 为什么在这种情况下不发生复制省略?,c++,copy-elision,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-

考虑以下代码:

#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;
}
我想知道为什么没有拷贝省略?我期待着更多类似的事情:

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

当我使用-fno-elide构造函数编译它时,正如预期的那样,
foo
返回值会发生复制省略

另外两个副本发生在
B
A
构造函数中。请注意,在输出中,它调用了两次
S(S const&)
,而对于
B(foo())
,人们希望看到至少一个
S(S&&)
。这是因为编译器已经消除了使用
S(S&&)
创建的额外副本。如果使用
-fno elide构造函数编译,您可以看到以下两个额外副本:

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构造函数
,则输出为:

S::S(std::string)
S::S(const S&)
S::S(const S&)
S::~S()
S::~S()
S::~S()
请参阅(用于函数参数的初始化):

首先,如果
T
是一个类类型,并且初始值设定项是一个
prvalue
表达式,其cv非限定类型与
T
是同一个类,则初始值设定项表达式本身(而不是它的临时物化)用于初始化目标对象:请参阅

通过引用接受,您可以避免剩余的两份副本:

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

S(string)c-tor如果我计算正确,没有省略,应该有四个复制构造函数调用,而不是两个。我建议您在一个调试器中运行,在复制构造函数上有一个断点,这样您就可以看到从何处调用它。这可能会给你一些关于正在发生的事情的提示。复制构造函数也可能会对更改
std::cout
的状态产生副作用吗?@ArdaAytekin-Huh?对不起,没关系。我弄糊涂了。实际上,在
B
std::move
中按值接受
S
也会减少拷贝数。正如您所说,这纯粹是因为构造函数签名。用户明确希望传递值。。。再一次为这一困惑感到抱歉。Maxim您说过foo发生了复制省略,那么为什么我在使用-fno-elide构造函数编译它时会得到相同的输出呢?此外,我仍然不明白为什么编译器不省略这两个副本?澄清一下:在C++17模式下编译时,-fno elide构造函数
没有任何区别,因为省略是必需的,不能关闭。在C++14模式下,省略是可选的,默认情况下是完成的,但可以使用
-fno elide构造函数关闭。不允许删除
A(s)
s{s}
所做的额外两个副本(在C++14或C++17中),因此编译器不会这样做。
S( string ) c-tor <--- foo
S&& c-tor         <--- A::s_
~S() d-tor
~S() d-tor