使用 std::unique_ptr 提升图形捆绑属性

Boost graph bundled properties with std::unique_ptr

std::unique_ptr 显示为带有 boost-graph 的捆绑 属性 的成员。

提供的例子可以试用 Live On Coliru.

由于我只对设计方面感兴趣,而不是该答案的算法部分,因此我提供了一个简化示例 Live On Coliru.

#include <iostream>
#include <fstream>

#include <boost/graph/adjacency_list.hpp>

struct custom_node{
    custom_node(std::string name, int capacity) : name(name), capacity(capacity) {}
    
    std::string name = "uninitialized";
    int capacity = -1;
};
void usage(){}

using namespace boost;

struct VertexProperties {
    int id;
    std::unique_ptr<custom_node> node;
};

typedef adjacency_list<vecS, vecS, directedS, VertexProperties> DirectedGraph;
typedef graph_traits<DirectedGraph>::vertex_descriptor custom_vertex;
typedef graph_traits<DirectedGraph>::edge_descriptor custom_edge;

int main() {
    DirectedGraph g;

    boost::add_vertex(g);    
    g[0].node = std::make_unique<custom_node>("inner", 2);
    
    ////boost::add_vertex(VertexProperties{0, std::make_unique<custom_node>("inner", 2)}, g); // compilation error
    
    std::cout << boost::num_vertices(g);
    std::cout << g[0].id << "\n";
    std::cout << g[0].node->name << "\n";
    std::cout << g[0].node->capacity << "\n";
}

可以通过boost::add_vertex(g)添加顶点,然后实例化std::unique_ptr

如果我试图通过在 add_vertex 中通过列表初始化创建具有所有属性的顶点来实现相同的目的,则会出现关于 implicitly-deleted copy constructor of 'VertexProperties' 的编译错误。 VertexProperties 当然删除了它的复制构造函数,因为 std::unique_ptr 删除了它的复制构造函数。

为什么一开始就需要复制构造函数来初始化列表?是不是有什么不明白的地方,或者这是boost-graph的缺点?

Why is the copy constructor is necessary for list initialization in the first place? Is there something I do not understand about it, or is this a shortcoming of boost-graph?

这不是一回事,也不是正在发生的事情。

Aside: in copy-initialization the assignment can be elided by the compiler, but operator= still needs to be accessible for it to be valid code.

但在这种情况下,只是库代码没有移动感知。您必须意识到“捆绑”属性是单独存储的。无论如何,bundle(任何 属性)都必须是默认可构造的(因此 add_vertex(g) 有效),因此通过始终分配给默认构造的 属性.[=30= 来简化实现]

因为它不是移动感知的,赋值不会转发右值,所以不会编译。

选项

  1. 链接的答案已经显示:

    if(g[v].node_logic) {
        g[v].node.reset(new custom_node(g[v].vertex_name, 0, standby, normal));
    }
    
  2. 更多选项:

    VertexProperties props {0,
        std::make_unique<custom_node>("inner", 2)};
    auto vd = boost::add_vertex(g);
    g[vd] = std::move(props);
    
  3. 收拾你的狗屎!您可以创建您喜欢的任何界面:

    auto add_vertex = [&g](VertexProperties&& props) {
        auto vd = boost::add_vertex(g);
        g[vd] = std::move(props);
        return vd;
    };
    
    add_vertex({0, std::make_unique<custom_node>("inner", 2)});
    
  4. 你甚至可以详细说明:

    auto add_vertex = [&g](int id, std::string name, int capacity = -1) {
        auto vd = boost::add_vertex(g);
        g[vd] = { id, std::make_unique<custom_node>(name, capacity) };
        return vd;
    };
    
    add_vertex(0, "inner", 2);
    add_vertex(1, "outer", 3);
    add_vertex(2, "other");
    

以上所有选项Live On Coliru

如果你问我,无论如何后一个界面要好得多。

如果您愿意,可以使用 ADL 使其可供其他人使用:

Live On Coliru

#include <boost/graph/adjacency_list.hpp>
#include <fstream>
#include <iostream>
#include <iomanip>
#include <memory>

namespace MyLib { // for ADL demo
    struct custom_node {
        custom_node(std::string name, int capacity)
            : name(std::move(name)), capacity(capacity) {}

        std::string name = "uninitialized";
        int capacity = -1;

        friend std::ostream& operator<<(std::ostream& os, custom_node const& cn) {
            return os << "{" << std::quoted(cn.name) << ", " << cn.capacity << "}";
        }
    };

    struct VertexProperties {
        int id{};
        std::unique_ptr<custom_node> node;

        friend std::ostream& operator<<(std::ostream& os, VertexProperties const& vp) {
            os << vp.id;
            if (vp.node)
                os << ", " << *vp.node;
            return os;
        }
    };

    template <typename G>
    auto add_vertex(G& g, int id, const std::string& name, int capacity = -1) {
        auto vd = boost::add_vertex(g);
        g[vd] = { id, std::make_unique<custom_node>(name, capacity) };
        return vd;
    }
}

using Graph = boost::adjacency_list<boost::vecS, boost::vecS, boost::directedS, MyLib::VertexProperties>;
using custom_vertex = Graph::vertex_descriptor;
using custom_edge = Graph::edge_descriptor;

int main() {
    Graph g;

    add_vertex(g, 10, "inner", 2);
    add_vertex(g, 11, "outer", 3);
    add_vertex(g, 12, "other");

    for (auto vd : boost::make_iterator_range(vertices(g))) {
        std::cout << g[vd] << "\n";
    }
}

版画

10, {"inner", 2}
11, {"outer", 3}
12, {"other", -1}

有人提到设计?

如果您想要的只是具有 optional/lazy 结构的独特所有权,为什么不呢:

struct VertexProperties {
    int id{};
    std::optional<custom_node> node;
};

甚至只是

struct VertexProperties {
    int id{};
    custom_node node;
};

所有权语义将相同,但没有成本:

Graph g;

add_vertex({10, custom_node{"inner", 2}}, g);
add_vertex({11, custom_node{"outer", 3}}, g);
add_vertex({12, custom_node{"other"}}, g);

这只是使用来自 BGL 的标准 boost::add_vertex 重载。没有 optional<> 它可以变得更简单:

add_vertex({10, {"inner", 2}}, g);
add_vertex({11, {"outer", 3}}, g);
add_vertex({12, {"other"}}, g);

还有Live On Coliru (without std::optional: Live)

#include <boost/graph/adjacency_list.hpp>
#include <fstream>
#include <iostream>
#include <iomanip>
#include <memory>
#include <optional>

struct custom_node {
    custom_node(std::string name, int capacity = -1)
        : name(std::move(name)), capacity(capacity) {}

    std::string name = "uninitialized";
    int capacity = -1;

    friend std::ostream& operator<<(std::ostream& os, custom_node const& cn) {
        return os << "{" << std::quoted(cn.name) << ", " << cn.capacity << "}";
    }
};

struct VertexProperties {
    int id{};
    std::optional<custom_node> node;

    friend std::ostream& operator<<(std::ostream& os, VertexProperties const& vp) {
        os << vp.id;
        if (vp.node) os << ", " << *vp.node;
        return os;
    }
};

using Graph = boost::adjacency_list<boost::vecS, boost::vecS, boost::directedS, VertexProperties>;
using custom_vertex = Graph::vertex_descriptor;
using custom_edge = Graph::edge_descriptor;

int main() {
    Graph g;

    add_vertex({10, custom_node{"inner", 2}}, g);
    add_vertex({11, custom_node{"outer", 3}}, g);
    add_vertex({12, custom_node{"other"}}, g);

    for (auto vd : boost::make_iterator_range(vertices(g))) {
        std::cout << g[vd] << "\n";
    }
}

版画

10, {"inner", 2}
11, {"outer", 3}
12, {"other", -1}