什么时候分配返回值会产生副本?

问题描述 投票:0回答:1

下面

int main
中有六行正在创建和初始化变量;在这些场景中的哪些(如果有)中,会为作业创建副本?

此外,在

sixthMap
的情况下,是否会发生任何奇怪的事情,因为我们使用带有&符号的自动推导,但它调用的函数也返回一个引用?

std::unordered_map<std::string, std::vector<std::string>>
createStdObject()
{
    std::unordered_map<std::string, std::vector> newUnorderedMap
    {
        {"A", {"first", "vector", "in", "map"}, 
        {"B", {"second", "vector", "in", "map"}
    };

    return newUnorderedMap;
}

std::unordered_map<std::string, std::vector<std::string>>&
createStdObjectWithReference()
{
    std::unordered_map<std::string, std::vector> newUnorderedMap
    {
        {"A", {"first", "vector", "in", "map"}, 
        {"B", {"second", "vector", "in", "map"}
    };

    return newUnorderedMap;
}

int main()
{
    std::unordered_map<std::string, std::vector> firstMap = newUnorderedMap();
    std::unordered_map<std::string, std::vector> secondMap = createStdObjectWithReference();
    
    const auto thirdMap = newUnorderedMap();
    const auto& fourthMap = newUnorderedMap();

    const auto fifthMap = createStdObjectWithReference();
    const auto& sixthMap = createStdObjectWithReference();
    
}

根据我有限的知识,我的猜测是:

  1. firstMap
    thirdMap
    可以是
    newUnorderedMap()
  2. 返回值的副本
  3. 所有其他都不会导致副本,而是移动/交换
  4. sixthMap
    自动声明中的&符号基本上会被忽略,并且
    sixthMap
    在类型和行为方面与
    fifthMap
    相同

另一方面,在编译时本身显然没有任何返回值实际上需要被复制,因此我认为没有理由它们不应该全部导致移动分配

c++ c++17 return-value
1个回答
0
投票

忽略你的代码有很多(我认为是)打字错误,你无法理解的是 C++ 中最基本的概念之一:对象生命周期。用不精确的方式简单解释一下(为了理解):

在堆栈上(即本地)创建的每个对象都有其作用域。一旦退出该范围,在该范围内创建的所有对象都将被销毁。此外,每个函数都是一个作用域,这意味着函数中创建的每个对象一旦到达函数末尾就会停止存在。因此,在函数内部创建的对象“比函数更长寿”的唯一方法是将它们复制/移动到函数外部的某个地址。

看似例外的是名为“复制省略”的东西。当您的函数按值返回,并且返回的对象是在该函数中创建的时,将不会涉及复制/移动,并且看起来您新创建的对象逃脱了其范围。然而,实际上,您的对象实际上是在调用站点上创建的,位于该函数的范围之外,而函数仅用于初始化已创建的对象。粗略地说,这就是复制省略的意思。它归结为复制指针,但没有调用构造函数。 因此,有以下代码:

#include <cstdio> class Lifetime { public: Lifetime() { std::puts("default ctor"); } Lifetime(Lifetime&&) { std::puts("move ctor"); } Lifetime(const Lifetime&) { std::puts("copy ctor"); } ~Lifetime() { std::puts("destructor"); } }; Lifetime make_lifetime() { Lifetime l{}; // Lifetime object initialized return l; // Lifetime object not destroyed because created outside the function scope } const Lifetime& make_lifetime_ref() { Lifetime l{}; // Lifetime object created return l; // Lifetime object destroyed } int main() { const auto lifetime1 = make_lifetime(); // Lifetime object created; copy ctor not called! // 'make_lifetime_ref' returns a reference to already destroyed object // const auto lifetime2 = make_lifetime_ref(); // oops! undefined behaviour: return 0; }

会产生这样的输出:

$ default ctor $ destructor

如您所见,不涉及复制。

请注意,

auto

与此无关,即它不会影响复制。因此,在您的情况下,创建

firstMap
thirdMap
fourthMap
不会涉及复制,而其他三个则会导致未定义的行为。另请注意,这都与局部变量有关,其他规则有一些不同。
    

© www.soinside.com 2019 - 2024. All rights reserved.