在 std::map 中搜索时堆栈溢出

Posted

技术标签:

【中文标题】在 std::map 中搜索时堆栈溢出【英文标题】:stack overflow when searching in std::map 【发布时间】:2014-04-13 14:11:48 【问题描述】:

这段代码在运行时由于某种原因导致堆栈溢出异常:

neuralnetwork::CPerceptron::inputEvent(const neuralnetwork::IConnection * origin, double value)
    std::map<std::reference_wrapper<const IConnection>, float64_t, _CPerceptronComparator>::iterator it = m_inputValues.find( std::ref( *origin ) );
    if ( it == m_inputValues.end() )
    
        throw "Some error";
    
    ...

_CPerceptronComparator 看起来像这样(它是必需的,因为 std::ref 没有运算符

class _CPerceptronComparator

public:
_CPerceptronComparator()



bool operator()( const std::reference_wrapper<const neuralnetwork::IConnection *> & val1,
                 const std::reference_wrapper<const neuralnetwork::IConnection *> & val2 ) const

    return (val1.get()) < (val2.get());


bool operator()( const std::reference_wrapper<const neuralnetwork::IConnection> & val1,
                 const std::reference_wrapper<const neuralnetwork::IConnection> & val2 ) const

    return &( val1.get() ) < &( val2.get() );


bool operator()( const std::reference_wrapper<neuralnetwork::IConnection> & val1,
                 const std::reference_wrapper<neuralnetwork::IConnection> & val2 ) const

    return &( val1.get() ) < &( val2.get() );

;

我添加了更多代码,因此也许您可以提供帮助。这就是我添加输入连接的方式:

 void CPerceptron::addInputConnection( IConnection * inConn )
 
    m_outConnections.insert( std::ref(*inConn) );

    m_inputValues.insert( std::pair<std::reference_wrapper<IConnection>, float64_t>( std::ref( *inConn ), 0.0f ) );
    m_isInputReady.insert( std::pair<std::reference_wrapper<IConnection>, bool>( std::ref( *inConn ), false ) );
 

注意:我知道 std::make_pair 更容易使用,但我更换了它,希望它会导致我的问题。你知道,它不可能看穿它返回的那种类型。此解决方案可能更难阅读,但很简单。

错误:Neural Network.exe 中 0x002C2EC9 处的未处理异常:0xC00000FD:堆栈溢出(参数:0x00000001、0x00102FFC)。

调用栈:

Neural Network.exe!std::_Iterator_base12::_Orphan_me() Line 192 C++
Neural Network.exe!std::_Iterator_base12::_Adopt(const std::_Container_base12 * _Parent) Line 165   C++
Neural Network.exe!std::_Iterator_base12::operator=(const std::_Iterator_base12 & _Right) Line 129  C++
Neural Network.exe!std::_Iterator_base12::_Iterator_base12(const std::_Iterator_base12 & _Right) Line 121   C++
Neural Network.exe!std::_Iterator012<std::bidirectional_iterator_tag,std::pair<std::reference_wrapper<neuralnetwork::IConnection const > const ,double>,int,std::pair<std::reference_wrapper<neuralnetwork::IConnection const > const ,double> const *,std::pair<std::reference_wrapper<neuralnetwork::IConnection const > const ,double> const &,std::_Iterator_base12>::_Iterator012<std::bidirectional_iterator_tag,std::pair<std::reference_wrapper<neuralnetwork::IConnection const > const ,double>,int,std::pair<std::reference_wrapper<neuralnetwork::IConnection const > const ,double> const *,std::pair<std::reference_wrapper<neuralnetwork::IConnection const > const ,double> const &,std::_Iterator_base12>(const std::_Iterator012<std::bidirectional_iterator_tag,std::pair<std::reference_wrapper<neuralnetwork::IConnection const > const ,double>,int,std::pair<std::reference_wrapper<neuralnetwork::IConnection const > const ,double> const *,std::pair<std::reference_wrapper<neuralnetwork::IConnection const > const ,double> const &,std::_Iterator_base12> & __that)    C++
Neural Network.exe!std::_Tree_unchecked_const_iterator<std::_Tree_val<std::_Tree_simple_types<std::pair<std::reference_wrapper<neuralnetwork::IConnection const > const ,double> > >,std::_Iterator_base12>::_Tree_unchecked_const_iterator<std::_Tree_val<std::_Tree_simple_types<std::pair<std::reference_wrapper<neuralnetwork::IConnection const > const ,double> > >,std::_Iterator_base12>(const std::_Tree_unchecked_const_iterator<std::_Tree_val<std::_Tree_simple_types<std::pair<std::reference_wrapper<neuralnetwork::IConnection const > const ,double> > >,std::_Iterator_base12> & __that)   C++
Neural Network.exe!std::_Tree_const_iterator<std::_Tree_val<std::_Tree_simple_types<std::pair<std::reference_wrapper<neuralnetwork::IConnection const > const ,double> > > >::_Tree_const_iterator<std::_Tree_val<std::_Tree_simple_types<std::pair<std::reference_wrapper<neuralnetwork::IConnection const > const ,double> > > >(const std::_Tree_const_iterator<std::_Tree_val<std::_Tree_simple_types<std::pair<std::reference_wrapper<neuralnetwork::IConnection const > const ,double> > > > & __that)    C++
Neural Network.exe!std::_Tree_iterator<std::_Tree_val<std::_Tree_simple_types<std::pair<std::reference_wrapper<neuralnetwork::IConnection const > const ,double> > > >::_Tree_iterator<std::_Tree_val<std::_Tree_simple_types<std::pair<std::reference_wrapper<neuralnetwork::IConnection const > const ,double> > > >(const std::_Tree_iterator<std::_Tree_val<std::_Tree_simple_types<std::pair<std::reference_wrapper<neuralnetwork::IConnection const > const ,double> > > > & __that)  C++
Neural Network.exe!std::_Tree<std::_Tmap_traits<std::reference_wrapper<neuralnetwork::IConnection const >,double,neuralnetwork::_CPerceptronComparator,std::allocator<std::pair<std::reference_wrapper<neuralnetwork::IConnection const > const ,double> >,0> >::find(const std::reference_wrapper<neuralnetwork::IConnection const > & _Keyval) Line 1553  C++
Neural Network.exe!neuralnetwork::CPerceptron::inputEvent(const neuralnetwork::IConnection * origin, double value) Line 94  C++

请注意,如果我调试地图,它应该包含一个元素。 (或者至少它的大小告诉我它有 1 个元素。)

无论我使用什么类型的迭代来遍历地图,它都会在第一次查询时出错。我使用 Visual Studio 2013。

我现在真的很无助。

抱歉,格式错误。

【问题讨论】:

此时的堆栈跟踪是什么? 您可能有兴趣知道_CPerceptronComparator 不是您在C++ 中使用的合法标识符。保留以下划线和大写字母开头的名称。 您发布的代码看起来不错,不应该给您堆栈溢出。您的问题很可能出在其他地方,可能是缓冲区溢出或写入某处的无效引用正在破坏堆栈。 添加了更多信息。我只是为新读者注意它,所以他们不会立即转身。 您是在堆栈上声明大型数组吗?这可能会触发溢出。 【参考方案1】:

问题是由无限循环引起的(实际上我见过的堆栈溢出的唯一原因)。

void CPerceptron::addInputConnection( IConnection * inConn )

    m_outConnections.insert( std::ref(*inConn) );

    m_inputValues.insert( std::pair<std::reference_wrapper<IConnection>, float64_t>( std::ref( *inConn ), 0.0f ) );
    m_isInputReady.insert( std::pair<std::reference_wrapper<IConnection>, bool>( std::ref( *inConn ), false ) );

我在一个名为 addInputConnection 的函数中插入到 m_outConnections 中,该函数在图中(在神经网络中)创建了一个圆圈,因此传播结果导致了无限循环(传播函数不在帖子中)。

这是一个很好的例子,说明这么容易错过这么小的东西,但在其他方面无用的帖子。删除它 IMO。

【讨论】:

【参考方案2】:

我已经获取了您的代码并创建了一个可以编译和运行的模型。如果在 gcc 4.8.1 下编译失败,无法进行模板替换。预感这与引用包装器的使用有关(并且您使用的是不同的编译器和标准库),我为 IConnection 类定义了一个 operator

你可以看到code in coliru here。

#include <iostream>
#include <map>
#include <stdexcept>

typedef double float64_t;

namespace neuralnetwork 

    class IConnection 
        float64_t _value = 0.0;
    public:
        IConnection( float64_t v ) : _value(v) 
        float64_t get() const  return _value; 
        friend inline bool operator< ( const IConnection & lhs, const IConnection & rhs )  return lhs.get() < rhs.get(); 
    ;

    class CPerceptron 
        std::map<const IConnection, float64_t>
        m_inputValues, m_isInputReady;
        public:
        void inputEvent(const neuralnetwork::IConnection * origin, double value);
        void addInputConnection( IConnection * inConn );
    ;

    void CPerceptron::addInputConnection( IConnection * inConn )
    
        m_inputValues.insert( std::pair<IConnection, float64_t>( *inConn, 0.0f ) );
    

 // namespace neuralnetwork

void neuralnetwork::CPerceptron::inputEvent(const neuralnetwork::IConnection * origin, double value)

    std::map< const neuralnetwork::IConnection, float64_t >::iterator
        it = m_inputValues.find( *origin );
    if ( it == m_inputValues.end() )
    
        throw std::runtime_error("Some error");
    
//   ...


int main( int argc, char *argv[] )

    neuralnetwork::IConnection ic( 1.0 );
    neuralnetwork::CPerceptron cp;
    cp.addInputConnection( & ic );
    cp.inputEvent( & ic, 2.0 );
    std::cout << "No stack overflow here!" << std::endl;
    return 0;

这是我得到的输出

g++-4.8 -std=c++11 -O2 -Wall -pedantic -pthread main.cpp && ./a.out
No stack overflow here!

更新

根据 OP 的观察,我创建了两个使用对象地址在地图中排序的版本:

在堆栈上创建的对象(失​​败):

http://coliru.stacked-crooked.com/a/02d1f262a75b1d25

在堆上创建的对象(失​​败):

http://coliru.stacked-crooked.com/a/728cf77040143f21

STL 将有序映射实现为二叉树,它使用复制构造函数将您的对象复制到映射中(二叉树)。它使用您提供的比较器(或运算符

使用 std::shared_ptr 有效(增加了一层间接性)。他们说计算机科学中的任何问题都可以通过一级间接来解决 - 似乎在这里有效。

http://coliru.stacked-crooked.com/a/6aa631675133289b

更新 2:

更简单,去掉get()函数,使用指向对象的指针作为map中的key,直接比较指针比operator&lt;中的小于:

http://coliru.stacked-crooked.com/a/f1376a39c8b287df

【讨论】:

可悲的是,即使它有效,它也不好。你按价值排序,我想按堆中的地址排序。值会发生变化——它会在每个学习周期中发生变化,毕竟这将是一个神经网络——并且在将对象放入地图后改变操作员的行为造成严重的后果问题。 @user2741959 啊,对不起,我错过了。这可能是错误的线索,实际上,让我考虑一下。 让我注意到您定义运算符的解决方案 @user2741959 STL 将有序的map 实现为二叉树,它使用复制构造函数将您的对象复制到映射中(二叉树)。它使用您提供的比较器(或运算符 更新 1 无效,更新 2 是正确的,但是我按照您的描述实现了 operator”作为指针是不确定的,正如我后来在互联网上发现的那样。无论如何请不要打扰,代码已经完美运行。

以上是关于在 std::map 中搜索时堆栈溢出的主要内容,如果未能解决你的问题,请参考以下文章

堆栈溢出相关问题算法[关闭]

运行递归线性搜索时出现堆栈溢出错误

使用按顺序遍历成员函数时引发异常(堆栈溢出)的问题

函数 堆栈溢出

什么是堆栈溢出?

使用 gp2c 时堆栈溢出,但在同一程序中直接使用 gp 时不会出现堆栈溢出 (PARI/GP)