代码之家  ›  专栏  ›  技术社区  ›  André T.

std::vector::返回引用时放置回bug(C++17)

  •  3
  • André T.  · 技术社区  · 7 年前

    我已经试着追踪一个bug 10个多小时了,现在我开始认为这个bug不可能在我这边。然而,我有一种感觉,可能是我忘记或误解了什么。

    我有一个类型为std::vector的类成员,名为temp\u materials,在构造函数中(当temp\u materials仍然为空时),此代码运行:

        Material &stonewallbug = temp_materials.emplace_back(resource_lib.get_shader("DeferredGeometryShader"));
        stonewallbug.set_texture("texture_diffuse1", resource_lib.get_texture("StonewallDiffuse"));
        stonewallbug.set_texture("texture_specular1", resource_lib.get_texture("StonewallSpecular"));
    
        Material &containerbug = temp_materials.emplace_back(resource_lib.get_shader("DeferredGeometryShader"));
        containerbug.set_texture("texture_diffuse1", resource_lib.get_texture("ContainerDiffuse"));
        containerbug.set_texture("texture_specular1", resource_lib.get_texture("ContainerSpecular"));
    
        Material stonewall1 = temp_materials[0];
        Material container1 = temp_materials[1];
    
        Material stonewall2 = stonewallbug;
        Material container2 = containerbug;
    

    如果复制过程中没有出错,stonewall1的内容应该等于stonewall2,container1到container2也应该一样,对吗?

    然而,我随后将它们全部插入到一个简单的向量中,该向量的内容将在稍后呈现:

        // Using temp_materials[0]
        auto stonewall1node = SceneNode(stonewall1, resource_lib.get_mesh("Cube"));
        stonewall1node.set_transform(glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, 0.0f)));
        m_scene_list.push_back(stonewall1node);
    
        // Using temp_materials[1]
        auto container1node = SceneNode(container1, resource_lib.get_mesh("Cube"));
        container1node.set_transform(glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 0.0f, 1.0f)));
        m_scene_list.push_back(container1node);
    
        // Using stonewallbug
        auto stonewall2node = SceneNode(stonewall2, resource_lib.get_mesh("Cube"));
        stonewall2node.set_transform(glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 1.0f, 0.0f)));
        m_scene_list.push_back(stonewall2node);
    
        // Using containerbug
        auto container2node = SceneNode(container2, resource_lib.get_mesh("Cube"));
        container2node.set_transform(glm::translate(glm::mat4(1.0f), glm::vec3(0.0f, 1.0f, 1.0f)));
        m_scene_list.push_back(container2node);
    

    现在,我希望有2个“容器”立方体相互堆叠,2个“石墙”立方体相互堆叠,但这是我得到的结果:

    Result1

    如果我移动线路 Material stonewall2 = stonewallbug; 在创建stonewallbug和containerbug之间,我得到了我期望的结果。

    #include <iostream>
    #include <vector>
    #include <string>
    
    
    int main() {
        std::vector<std::string> strings;
    
        std::string& ref1 = strings.emplace_back("1");
        std::string& ref2 = strings.emplace_back("2");
    
        std::cout << "ref1: " << ref1 << std::endl;
        std::cout << "ref2: " << ref2 << std::endl;
    
        std::cout << "strings[0]: " << strings[0] << std::endl;
        std::cout << "strings[1]: " << strings[1] << std::endl;
    
    
        return 0;
    }
    

    运行时会产生很多无意义的角色。但是,如果我在放置ref2之前输出ref1,它将输出预期结果。在…上 here 它说,向量的emplace_back应该返回对插入元素的引用,但对我来说,它似乎并没有正常工作。

    我是误解了什么,还是这只是一个 非常 g++7.1中的明显缺陷?

    编辑:我简直不敢相信我在这么明显的事情上花了这么多时间……:)

    1 回复  |  直到 7 年前
        1
  •  11
  •   Remy Lebeau    7 年前

    您必须小心容器元素的引用和迭代器。各种变异容器操作 引用和迭代器。细节因容器和操作而异,但对于 std::vector push_back emplace_back ),以及从除结束之外的任何内容中删除,通常会使引用和迭代器无效(但请参阅下文以了解避免这种情况的方法)。

    因此,在以下代码中,

    std::vector<T> v;
    
    T & a = v.emplace_back(a, b, c);
    T & b = v.emplace_back(x, y, z);
    

    的第二次调用 emplace_后退 使引用无效 a . (通过无效引用访问对象具有未定义的行为。)

    为了避免在 标准::矢量 ,使用 reserve capacity 设施:

    std::vector<T> v;
    v.reserve(2);
    T & a = v.emplace_back(a, b, c);
    T & b = v.emplace_back(x, y, z);
    

    末端插入 如果新的 size 小于或等于电流 .