簡體   English   中英

映射迭代器對值的復制省略

[英]Copy elision of map iterator pair value

在下面的MVE中, Get函數的返回值是否符合復制省略條件?

編輯

我對示例進行了一些更改。 在Debug和Release版本中都使用Visual Studio 2017,我在return語句上看到了副本構造。 希望這僅僅是因為我搞砸了可以幫助我進行調試的Type

#include <map>
#include <string>
#include <iostream>
#include <ostream>

struct Type
{
    Type()
    {
        std::cout << "Default construction\n";
    };

    explicit Type(std::string obj) : obj(std::move(obj))
    {
        std::cout << "Other construction\n";
    }
    ~Type() = default;

    Type(const Type& other) : obj{other.obj}
    {
        std::cout << "Copy construction\n";
    }

    Type(Type&& other) noexcept : obj{std::move(other.obj)}
    {
        std::cout << "Move constructor\n";
    }

    Type& operator=(const Type& other)
    {
        std::cout << "Copy assignment\n";
        if (this == &other)
            return *this;
        obj = other.obj;
        return *this;
    }

    Type& operator=(Type&& other) noexcept
    {
        std::cout << "Move assignment\n";
        if (this == &other)
            return *this;
        obj = std::move(other.obj);
        return *this;
    }

    friend std::ostream& operator<<(std::ostream& os, const Type& obj1)
    {
        return os << obj1.obj;
    }

    std::string obj;
};


std::map<std::string, Type> mVariables;

Type Get(const std::string& variableName)
{
    const auto variableIt = mVariables.find(variableName);
    if(variableIt==std::end(mVariables)) {
        throw std::runtime_error("Unknown variable requested.");
    }
    return variableIt->second;
}

int main()
{
    mVariables.emplace(std::make_pair("key", Type("value")));
    const auto value = Get("key");  
    std::cout << value;
    return 0;
}

上面的示例提供了以下輸出,這引起了一些關於make_pair問題,但是這里不做討論。 我想我的困惑是,在此示例中,什么阻止了復制省略的發生?

Other construction
Move constructor
Move constructor
Copy construction
value

我建議您在Compiler Explorer上玩。

我在GCC主干中看到的是,幾乎所有的map和string函數都被內聯了,剩下的唯一函數調用是對memcmp (用於內聯查找和字符串比較)以及對newmemcpy (對於返回的副本)。

lang干似乎沒有內聯。 map::find仍然存在,但仍然只調用newmemcpy

在C ++ 17 1之前, const auto value = Get("key");的語義const auto value = Get("key"); 是從返回的表達式variableIt->second復制初始化一個臨時對象,然后從該臨時對象復制初始化value 因此,本來基本上有兩個副本/動作。

通過根據N3797 [class.copy]第31段項目符號3:直接從variableIt->second構造value ,可以消除從臨時對象進行復制/移動的操作:

  • 當尚未綁定到引用(12.2)的臨時類對象將被復制/移動到具有相同cv-unqualified類型的類對象時,可以通過將臨時對象直接構造到目標中來省略復制/移動操作省略的副本/移動。

從C ++ 17開始,通過防止臨時對象在語義上實現,可以保證此副本。 const auto value = Get("key");的新語義 variableIt->second 2變為復制初始化value

無法刪除來自variableIt->second it- variableIt->second的副本,因為它不滿足return語句中出現的刪除副本的要求,即N3797 [class.copy]第31段bullet 1 3

  • 在具有類返回類型的函數的return語句中,當表達式是具有與函數返回類型相同的cv不合格類型的非易失性自動對象 (函數或catch子句參數除外)的名稱時,通過將自動對象直接構造到函數的返回值中,可以省略復制/移動操作

這是合理的,因為variableIt->second的生存期不會結束,並且可能在將來使用,因此無法將value優化為variableIt->second的別名,因此必須進行復制。


1自C ++ 17以來的唯一區別是第三段中提到的保證復制省略。 (在我看來)從C ++ 17之前的語義開始進行分析更為直接。

2在C ++ 17中,有多個規則組合在一起得出結論,對於這個問題並不重要,因此我從標准中引出了相應規則的引用。

3在C ++ 17中,此規則的措詞略有變化,而該規則本質上是相同的。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM