C++ 返回对向量中值的引用

Posted

技术标签:

【中文标题】C++ 返回对向量中值的引用【英文标题】:C++ returing a reference to a value in vector 【发布时间】:2018-10-24 00:19:16 【问题描述】:

给定以下程序:

#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 v1u,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 中的值与向量中的值不同。

我的问题是两个:

第一个函数的行为是否正确?是否是未定义的行为? 为什么第二个会改变向量?

【问题讨论】:

因为第二个有UB,第一个没有 @Slava 请将此作为答案。第二个的UB是多少? 第二个v 没有被移动,而是被复制,所以所有的引用都失效了。如果您明确移动v,它应该会修复它。 因为RVO导致第一个没有副本 是的,它被复制到,你也应该移动它,但是为了效率和一致性,但不移动 v 会由于地图中的无效引用而带来 UB。 【参考方案1】:

由于NRVO,您的第一个功能似乎可以工作:

在return语句中,当操作数是具有自动存储持续时间的非易失性对象的名称时,该对象不是函数参数或catch子句参数,并且属于同一类类型(忽略cv-资格)作为函数返回类型。这种复制省略的变体被称为 NRVO,“命名返回值优化”。

如果发生这种情况,则没有执行复制,并且地图中的引用仍然有效,但由于 NRVO 是一种优化,因此不能保证您只是幸运地工作。

在第二个函数中,您创建std::pair&lt;std::vector&lt;unsigned&gt;, std::unordered_map&lt;unsigned, unsigned&amp;&gt;&gt; 类型的临时对象,并使用左值vindex 对其进行初始化,因此为它们调用了复制构造函数,并且所有引用在原始v 时都无效被摧毁。修复可能是显式调用std::move

return std::move(v), index;

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

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

【讨论】:

最后感谢您的建议。在尝试复制这对配对后,我很快意识到了这一点 @smac89 np,在容器中引用通常不是一个好主意,但对矢量元素的引用更糟糕。 这是 NRVO,不能保证。

以上是关于C++ 返回对向量中值的引用的主要内容,如果未能解决你的问题,请参考以下文章

C++,成员函数返回对包含指向 const 对象的指针的向量的 const 引用

C++ - 返回指针或常量引用

如何在 C++ 中通过引用返回向量

如何从函数返回向量引用?

C++ 向量通过引用语法传递

C++:对象、对对象的引用、对带有和不带有函数的向量元素的引用——观察到的性能差异