[英]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
(用於內聯查找和字符串比較)以及對new
和memcpy
(對於返回的副本)。
lang干似乎沒有內聯。 map::find
仍然存在,但仍然只調用new
和memcpy
。
在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.