C++ 返回对向量中值的引用
C++ returing a reference to a value in vector
给定以下程序:
#include <iostream>
#include <utility>
#include <vector>
#include <unordered_map>
#ifdef WITHPAIR
auto get_state() {
std::pair<std::vector<unsigned>, std::unordered_map<unsigned, unsigned&>> st;
auto& v = st.first;
auto& index = st.second;
v.assign({1u,2u,3u,4u});
index.insert({0u, v[0]});
index.insert({1u, v[1]});
index.insert({2u, v[2]});
index.insert({3u, v[3]});
return st;
}
#else
std::pair<std::vector<unsigned>, std::unordered_map<unsigned, unsigned&>> get_state() {
std::vector v{1u,2u,3u,4u};
std::unordered_map<unsigned, unsigned&> index{
{0u, v[0]},
{1u, v[1]},
{2u, v[2]},
{3u, v[3]}
};
return {v, index};
}
#endif
auto main() -> int {
auto [v, index] = get_state();
// auto [v, index] = std::move(get_state());
std::cout << v[0] << " " << index.at(0) << std::endl;
v[0] = 5;
std::cout << v[0] << " " << index.at(0) << std::endl;
std::cout << v[1] << " " << index.at(1) << std::endl;
v[2] = 17;
std::cout << v[2] << " " << index.at(2) << std::endl;
std::cout << v[3] << " " << index.at(3) << std::endl;
return 0;
}
http://coliru.stacked-crooked.com/a/f9e528074ae78c03
不编译 -DWITHPAIR
以查看第二个函数的行为
函数有两个版本get_state
。
只有第一个函数似乎具有正确的行为,并且在从函数返回时实际上使该数据可用(正如您从链接程序中看到的那样);第二个函数不这样做,unordered_map 中的值与向量中的值不同。
我的问题是两个:
- 第一个函数的行为是否正确?是否是未定义的行为?
- 为什么第二个要改变向量?
由于 NRVO:
,您的第一个功能似乎可以正常工作
In a return statement, when the operand is the name of a non-volatile object with automatic storage duration, which isn't a function parameter or a catch clause parameter, and which is of the same class type (ignoring cv-qualification) as the function return type. This variant of copy elision is known as NRVO, "named return value optimization".
如果发生这种情况,则不会执行任何复制,并且地图中的引用仍然有效,但由于 NRVO 是一种优化,不能保证你只是运气好,所以这项工作有效。
在第二个函数中,您创建类型为 std::pair<std::vector<unsigned>, std::unordered_map<unsigned, unsigned&>>
的临时对象,并使用左值 v
和 index
对其进行初始化,因此会为它们和所有对象调用复制构造函数当原始 v
被销毁时,引用变得无效。修复可能是显式调用 std::move
:
return {std::move(v), index};
并且作为标准保证引用保持有效并将指向新向量中的元素,因此应该不再有 UB。 index
也应该移动以提高效率和一致性,但我保留了它,因为它与问题无关。
这是一种非常危险的编程方式,我会考虑不同的数据类型(可能是向量中的索引而不是引用)。
给定以下程序:
#include <iostream>
#include <utility>
#include <vector>
#include <unordered_map>
#ifdef WITHPAIR
auto get_state() {
std::pair<std::vector<unsigned>, std::unordered_map<unsigned, unsigned&>> st;
auto& v = st.first;
auto& index = st.second;
v.assign({1u,2u,3u,4u});
index.insert({0u, v[0]});
index.insert({1u, v[1]});
index.insert({2u, v[2]});
index.insert({3u, v[3]});
return st;
}
#else
std::pair<std::vector<unsigned>, std::unordered_map<unsigned, unsigned&>> get_state() {
std::vector v{1u,2u,3u,4u};
std::unordered_map<unsigned, unsigned&> index{
{0u, v[0]},
{1u, v[1]},
{2u, v[2]},
{3u, v[3]}
};
return {v, index};
}
#endif
auto main() -> int {
auto [v, index] = get_state();
// auto [v, index] = std::move(get_state());
std::cout << v[0] << " " << index.at(0) << std::endl;
v[0] = 5;
std::cout << v[0] << " " << index.at(0) << std::endl;
std::cout << v[1] << " " << index.at(1) << std::endl;
v[2] = 17;
std::cout << v[2] << " " << index.at(2) << std::endl;
std::cout << v[3] << " " << index.at(3) << std::endl;
return 0;
}
http://coliru.stacked-crooked.com/a/f9e528074ae78c03
不编译 -DWITHPAIR
以查看第二个函数的行为
函数有两个版本get_state
。
只有第一个函数似乎具有正确的行为,并且在从函数返回时实际上使该数据可用(正如您从链接程序中看到的那样);第二个函数不这样做,unordered_map 中的值与向量中的值不同。
我的问题是两个:
- 第一个函数的行为是否正确?是否是未定义的行为?
- 为什么第二个要改变向量?
由于 NRVO:
,您的第一个功能似乎可以正常工作In a return statement, when the operand is the name of a non-volatile object with automatic storage duration, which isn't a function parameter or a catch clause parameter, and which is of the same class type (ignoring cv-qualification) as the function return type. This variant of copy elision is known as NRVO, "named return value optimization".
如果发生这种情况,则不会执行任何复制,并且地图中的引用仍然有效,但由于 NRVO 是一种优化,不能保证你只是运气好,所以这项工作有效。
在第二个函数中,您创建类型为 std::pair<std::vector<unsigned>, std::unordered_map<unsigned, unsigned&>>
的临时对象,并使用左值 v
和 index
对其进行初始化,因此会为它们和所有对象调用复制构造函数当原始 v
被销毁时,引用变得无效。修复可能是显式调用 std::move
:
return {std::move(v), index};
并且作为标准保证引用保持有效并将指向新向量中的元素,因此应该不再有 UB。 index
也应该移动以提高效率和一致性,但我保留了它,因为它与问题无关。
这是一种非常危险的编程方式,我会考虑不同的数据类型(可能是向量中的索引而不是引用)。