首页 > 解决方案 > 使用 std::unique_ptr 提升图捆绑属性

问题描述

此答案显示 astd::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的缺点?

标签: c++boostunique-ptrboost-graph

解决方案


为什么列表初始化首先需要复制构造函数?有什么我不明白的地方,还是boost-graph的缺点?

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

另外:在复制初始化中,编译器可以省略赋值,但 operator= 仍然需要可访问才能使其成为有效代码。

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

由于它不是移动感知的,因此分配不会转发右值并且事情不会编译。

选项

  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 将其提供给其他人:

住在科利鲁

#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}

有人提到设计?

如果您想要的只是具有可选/惰性构造的唯一所有权,为什么不:

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);

这只是使用boost::add_vertexBGL 的标准重载。没有optional<>它会变得更简单:

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

Live On Coliru(没有 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}

推荐阅读