我正在努力更好地理解 C 的復雜性。我在 Jason Turner 的 C 最佳實踐中遇到了這個練習。
問題是這里是否正在創建副本。
std::map<std::string, int> get_map();
using element_type = std::pair<std::string, int>;
for (const element_type & : get_map())
{
}
我的回答是肯定的,因為我認為 std::map 沒有明確使用 std::pair 來表示鍵值對。所以也許隱含的底層鍵值對被復制到element_type?我在這里怎么樣?任何幫助,將不勝感激 :)。
uj5u.com熱心網友回復:
我試試這個簡單的代碼部分:
#include <map>
#include <string>
#include <iostream>
std::map<std::string, int> get_map()
{
std::map<std::string, int> m { {"CPU", 10}, {"GPU", 15} };
return m;
}
int main()
{
using element_type = std::pair<std::string, int>;
for (const element_type &e : get_map())
{
}
return 1;
}
它不會復制,而是移動了移動。我使用https://cppinsights.io/來獲取:
std::map<std::basic_string<char>, int, std::less<std::basic_string<char> >, std::allocator<std::pair<const std::basic_string<char>, int> > > get_map()
{
std::map<std::string, int> m = std::map<std::basic_string<char>, int, std::less<std::basic_string<char> >, std::allocator<std::pair<const std::basic_string<char>, int> > >{std::initializer_list<std::pair<const std::basic_string<char>, int> >{std::pair<const std::basic_string<char>, int>{"CPU", 10}, std::pair<const std::basic_string<char>, int>{"GPU", 15}}, std::less<std::basic_string<char> >(), std::allocator<std::pair<const std::basic_string<char>, int> >()} /* NRVO variable */;
return std::map<std::basic_string<char>, int, std::less<std::basic_string<char> >, std::allocator<std::pair<const std::basic_string<char>, int> > >(static_cast<std::map<std::basic_string<char>, int, std::less<std::basic_string<char> >, std::allocator<std::pair<const std::basic_string<char>, int> > > &&>(m));
}
int main()
{
using element_type = std::pair<std::basic_string<char>, int>;
{
std::map<std::basic_string<char>, int, std::less<std::basic_string<char> >, std::allocator<std::pair<const std::basic_string<char>, int> > > && __range1 = get_map();
std::_Rb_tree_iterator<std::pair<const std::basic_string<char>, int> > __begin1 = __range1.begin();
std::_Rb_tree_iterator<std::pair<const std::basic_string<char>, int> > __end1 = __range1.end();
for(; operator!=(__begin1, __end1); __begin1.operator ()) {
const std::pair<std::basic_string<char>, int> & e = std::pair<std::basic_string<char>, int>(__begin1.operator*());
}
}
return 1;
}
希望這有幫助。謝謝。
uj5u.com熱心網友回復:
回圈將在for內部使用迭代器。取消參考一個迭代器將生成一個value_type物件而不是一個物件的參考。因此,即使您自己的回圈指定了一個參考,它也將是對由迭代器(通過副本)創建并通過您的參考保持活動狀態的臨時物件的參考。
uj5u.com熱心網友回復:
在我的回答中,我想從字面上解決問題的標題:
如何知道何時創建副本?
這可以通過一個示例類很容易地實作,其中每個建構式都會產生一個輸出:
struct Data {
Data() { std::cout << "Data::Data()\n"; }
Data(const Data&) { std::cout << "Data::Data(const Data&)\n"; }
Data& operator=(const Data&) { std::cout << "Data::operator=(const Data&)\n"; return *this; }
Data(Data&&) { std::cout << "Data::Data(Data&&)\n"; }
Data& operator=(Data&&) { std::cout << "Data::operator=(Data&&)\n"; return *this; }
};
根據 cppreference.com,std::pair的復制建構式是默認的,即它只是呼叫其成員變數的復制建構式:
pair( const pair& p ) = default;
因此,一點 MCVE 可以很明顯地說明 OP 的錯誤:
#include <iostream>
#include <map>
struct Data {
Data() { std::cout << "Data::Data()\n"; }
Data(const Data&) { std::cout << "Data::Data(const Data&)\n"; }
Data& operator=(const Data&) { std::cout << "Data::operator=(const Data&)\n"; return *this; }
Data(Data&&) { std::cout << "Data::Data(Data&&)\n"; }
Data& operator=(Data&&) { std::cout << "Data::operator=(Data&&)\n"; return *this; }
};
#define DEBUG(...) std::cout << #__VA_ARGS__ << ";\n"; __VA_ARGS__ ; std::cout << std::endl
int main()
{
DEBUG(std::map<int, Data> aMap = { { 1, Data() }, { 2, Data() } });
// done like OP -> copy assignment
DEBUG(for (const std::pair<int, Data>& value : aMap) {
std::cout << value.first << '\n';
});
// without copy assignment
DEBUG(for (const std::pair<const int, Data>& value : aMap) {
std::cout << value.first << '\n';
});
DEBUG(for (const std::map<int, Data>::value_type& value : aMap) {
std::cout << value.first << '\n';
});
}
輸出:
std::map<int, Data> aMap = { { 1, Data() }, { 2, Data() } };
Data::Data()
Data::Data(Data&&)
Data::Data()
Data::Data(Data&&)
Data::Data(const Data&)
Data::Data(const Data&)
for (const std::pair<int, Data>& value : aMap) { std::cout << value.first << '\n'; };
Data::Data(const Data&)
1
Data::Data(const Data&)
2
for (const std::pair<const int, Data>& value : aMap) { std::cout << value.first << '\n'; };
1
2
for (const std::map<int, Data>::value_type& value : aMap) { std::cout << value.first << '\n'; };
1
2
關于大腸桿菌的演示
因此,對于第一個回圈(使用),在每次迭代中都會呼叫std::pair<int, Data>的復制建構式。Data
不會發生這種情況,std::pair<const int, Data>
也不會發生這種情況std::map<int, Data>::value_type。
另一個簡單的替代方法可能是使用auto:
for (const auto& value : aMap) {
std::cout << value.first << '\n';
}
此外,我想用更短的 MCVE 提及 g 的診斷:
#include <iostream>
#include <map>
struct Data { };
int main()
{
std::map<int, Data> aMap = { { 1, Data() } };
for (const std::pair<int, Data>& value : aMap) std::cout << value.first << '\n';
}
輸出:
main.cpp: In function 'int main()':
main.cpp:9:36: warning: loop variable 'value' of type 'const std::pair<int, Data>&' binds to a temporary constructed from type 'std::pair<const int, Data>' [-Wrange-loop-construct]
9 | for (const std::pair<int, Data>& value : aMap) std::cout << value.first << '\n';
| ^~~~~
main.cpp:9:36: note: use non-reference type 'const std::pair<int, Data>' to make the copy explicit or 'const std::pair<const int, Data>&' to prevent copying
關于大腸桿菌的演示
轉載請註明出處,本文鏈接:https://www.uj5u.com/gongcheng/493995.html
標籤:C
