微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

为什么编译器会在某些优化级别警告未初始化的边迭代器?

如何解决为什么编译器会在某些优化级别警告未初始化的边迭代器?

我已尽我所能最小化了可重现的示例。在其中,我遍历了 boost 图中的所有边,并收到了一个我不明白的警告。请向我解释为什么我会收到此警告,也许还有为什么它只出现在某些优化级别。

/*
 *  reproducing the warning about maybe uninitialized edge iterator
 */
#include <vector>
#include <iostream>
#include <boost/graph/adjacency_list.hpp>

typedef boost::adjacency_list_traits<boost::vecS,boost::vecS,boost::directedS> traits;
typedef boost::adjacency_list<boost::vecS,boost::directedS,boost::no_property,boost::property<boost::edge_capacity_t,long>> graph;

typedef traits::vertex_descriptor vertex_desc;
typedef traits::edge_descriptor edge_desc;
typedef boost::graph_traits<graph>::edge_iterator edge_iterator;

int main(){
    // build a graph
    graph G(10);
    // get its capacity map
    auto c_map = boost::get(boost::edge_capacity,G);
    // get a handle to the first vertex
    vertex_desc source = boost::vertex(0,G);
    // add an edge,with a capacity of 1
    edge_desc e = boost::add_edge(boost::vertex(0,G),boost::vertex(1,G).first;
    c_map[e] = 1;
    // loop over all edges in the graph
    std::pair<edge_iterator,edge_iterator> eip = boost::edges(G);
    for (edge_iterator eit = eip.first; eit != eip.second; eit++){
        edge_desc e = *eit;
        vertex_desc a = boost::source(e,G);
        vertex_desc b = boost::target(e,G);
        bool source_involved = ((a == source) || (b == source));
        std::cout << (source_involved ? "yes" : "no") << std::endl;
    }
}

使用 -O0-O1 编译,没有警告:

g++ -std=c++14 -Wall -Wextra -O0 repro.cpp -o repro.exe

使用 -O2 编译,我收到此警告:

# g++ -std=c++14 -Wall -Wextra -O2 repro.cpp -o repro.exe
repro.cpp: In function 'int main()':
repro.cpp:28:24: warning: '*((void*)& eit +48)' may be used uninitialized in this function [-Wmaybe-uninitialized]
     for (edge_iterator eit = eip.first; eit != eip.second; eit++){
                        ^~~

并用 -O3,-O4,-O5 编译我得到了类似的警告,但很难看:

# g++ -std=c++14 -Wall -Wextra -O3 repro.cpp -o repro.exe
repro.cpp: In function 'int main()':
repro.cpp:28:24: warning: '*((void*)(& eit)+32).__gnu_cxx::__normal_iterator<boost::detail::stored_edge_property<long unsigned int,long int> >*,std::vector<boost::detail::stored_edge_property<long unsigned int,long int> >,std::allocator<boost::detail::stored_edge_property<long unsigned int,long int> > > > >::_M_current' may be used uninitialized in this function [-Wmaybe-uninitialized]
     for (edge_iterator eit = eip.first; eit != eip.second; eit++){

我的 g++ --version

g++ (Ubuntu 8.4.0-1ubuntu1~18.04) 8.4.0

我在 docker 中运行

# uname -a
Linux 88157d45c773 5.4.0-58-generic #64~18.04.1-Ubuntu SMP Wed Dec 9 17:11:11 UTC 2020 x86_64 x86_64 x86_64 GNU/Linux

警告确实出现在 -std=c++11-std=c++14 中,但似乎没有出现在 -std=c++17 中。

这个警告告诉我什么?有什么问题?

解决方法

正如我所评论的,这可能是特定编译器版本的编译器问题,其中内联 + 死代码省略会导致虚假的未使用变量警告。

我认为查找是否存在与修复相关的错误报告并没有什么好处。相反,让我向您展示在 c++14 中编写代码的更优雅的方法。

并非旨在回答所提出的问题。尽管纯属巧合,您遇到的特定警告可能已经消失。

我发布了灵感/教育价值的答案,因为我看到很多严重落后于时代的副本/过去的 BGL。

Live On Wandbox

#include <boost/graph/adjacency_list.hpp>
#include <iostream>
#include <vector>

using capacity_prop = boost::property<boost::edge_capacity_t,long>;

using graph = boost::adjacency_list<
                    boost::vecS,boost::vecS,boost::directedS,boost::no_property,capacity_prop
                >;

int main() {
    graph G(10);
    auto add = [&G](auto a,auto b,auto c) {
        add_edge(vertex(a,G),vertex(b,capacity_prop{c},G);
    };
    add(0,1,1);
    add(1,2,2);
    add(1,3,3);
    add(1,7,4);
    add(1,8,5);
    add(2,5,6);
    add(5,7);
    add(5,8);
    add(7,9);

    auto const s = vertex(0,G);

    for (auto e : boost::make_iterator_range(edges(G))) {
        auto a = source(e,G);
        auto b = target(e,G);
        std::cout << e << " involves " << s << "? "
            << std::boolalpha << (a == s || b == s) << std::endl;
    }
}

印刷品

(0,1) involves 0? true
(1,2) involves 0? false
(1,3) involves 0? false
(1,7) involves 0? false
(1,8) involves 0? false
(2,5) involves 0? false
(5,3) involves 0? false
(5,0) involves 0? true
(7,0) involves 0? true

C++17 奖励

这样可以更自然地编写初始化代码

for (auto [a,b,c] : { std::tuple
        {0,1},{1,2},3},4},5},{2,6},{5,7},8},{7,9},})
{
    add_edge(vertex(a,G);
}

逻辑奖励

如果你真的只想找到涉及顶点 s 的边集,你可以这样写,可能会更有效率:

Live On Wandbox

auto const s = vertex(2,G); // different example
auto [f,l] = out_edges(s,G);
edge_set involved(f,l);

for (auto e : make_iterator_range(edges(G)))
    if (target(e,G) == s)
        involved.insert(e);

for (auto e : involved)
    std::cout << e << " ";

印刷品

(1,2) (2,5) 

正如人们所料。

性能提示

如果您可以调整图形模型,那么通过更改它来维护 adjacency_list 的双向边可以获得更好的性能:

std::set<graph::edge_descriptor> involved;

auto insert = [&](auto range) { involved.insert(range.first,range.second); };
insert(out_edges(s,G));
insert(in_edges(s,G));

或者,实际上,直接打印它们:

for (auto e : make_iterator_range(out_edges(s,G)))
    std::cout << e << " ";
for (auto e : make_iterator_range(in_edges(s,G)))
    std::cout << e << " ";

看到它Live Ob Wandbox

打印相同。

这是否会提高您的应用程序的性能主要取决于您是否在图表上有更多的突变或更多的查询。

PS

这是我为示例制作的图表以供参考

enter image description here

这是做的结果

boost::dynamic_properties dp;
dp.property("node_id",get(boost::vertex_index,G));
dp.property("label",get(boost::edge_capacity,G));
write_graphviz_dp(std::cout,G,dp);

并使用 dot(例如 online

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其他元素将获得点击?
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。)
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbcDriver发生异常。为什么?
这是用Java进行XML解析的最佳库。
Java的PriorityQueue的内置迭代器不会以任何特定顺序遍历数据结构。为什么?
如何在Java中聆听按键时移动图像。
Java“Program to an interface”。这是什么意思?