在使用std::vector构建的递归结构时,使用auto会导致对象被过早销毁

编程语言 2026-07-10

我在处理一个树状结构,并写了类似这样的代码:

#include <iostream>
#include <string>
#include <string_view>
#include <vector>

struct Node
{
    std::string _name;
    std::vector<Node> _subnodes;

    Node(const std::string_view sv) : _name(sv), _subnodes() {
        //std::cout << "construct '" << _name << "'" << std::endl;
    }
    ~Node() {
        //std::cout << "destruct '" << _name << "'" << std::endl;
    }

    Node& add(const std::string_view sv) {
        return _subnodes.emplace_back(sv);
    }

    void dump(size_t indent=0) const {
        for (size_t i=0; i<indent; i++) {
            std::cout << "  ";
        }
        std::cout << _name << std::endl;

        for (const auto& n : _subnodes) {
            n.dump(indent+1);
        }
    }
};

int main()
{
    auto root = Node("root");

    auto alpha = root.add("alpha");
    alpha.add("one");
    alpha.add("two");
    alpha.add("three");

    auto beta  = root.add("beta");
    beta.add("four");
    beta.add("five");
    beta.add("six");

    auto gamma = root.add("gamma");
    gamma.add("seven");
    gamma.add("eight");
    gamma.add("nine");

    root.dump();

    return 0;
}

Compiler Explorer

出于某种原因,子节点“one”、……、“nine”被过早销毁,未在转储中显示。

为什么会这样?似乎与所使用的编译器无关。

解决方案

在如下所示的代码行:

auto alpha = root.add("alpha");

alpha 实际上是一个 拷贝,因为默认情况下不会推断出引用。
因此添加子节点并非针对目标节点,而是对它的一个拷贝进行的。

如果把它改为:

//---v---------------------------
auto & alpha = root.add("alpha");

(并对 betagamma 进行同样的修改。)
你将得到预期的结果。

实时演示 - Compiler Explorer

然而—
正如 @Eljay commented 所指出的,emplace_back 可能在 std::vector 需要重新分配时使现有引用失效(由于容量不足)。
你可以通过在此之前使用 std::vector::reserve 来避免重新分配的需要,并确保容量足够。

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章