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&>> 的临时对象,并使用左值 vindex 对其进行初始化,因此会为它们和所有对象调用复制构造函数当原始 v 被销毁时,引用变得无效。修复可能是显式调用 std::move

return {std::move(v), index};

并且作为标准保证引用保持有效并将指向新向量中的元素,因此应该不再有 UB。 index 也应该移动以提高效率和一致性,但我保留了它,因为它与问题无关。

这是一种非常危险的编程方式,我会考虑不同的数据类型(可能是向量中的索引而不是引用)。