【问题标题】:Odd lambda issue (compiler bug?)奇怪的 lambda 问题(编译器错误?)
【发布时间】:2012-06-28 23:31:41
【问题描述】:

我有一段代码在运行时因各种奇怪的内存损坏而失败。我已将其范围缩小到这部分代码:

List<CollisionBlock> WorldClient::getCollisionBlocks(RectF const& boundBox, bool doSort, Vec2F sortCenter) const {
  auto res = m_collisionGenerator.getPolys(boundBox);

  if (doSort) {
    sort(res, [=](CollisionBlock const& block1, CollisionBlock const& block2) {
    return magSquared(sortCenter - block1.poly.center()) < magSquared(sortCenter - block2.poly.center());
      });
  }

  return res;
}

如果我从 lambda 中删除 const&amp;,则代码可以正常工作。我不知道为什么。我想知道我是否遇到了编译器错误,或者我是否忽略了一些明显的东西。

这是 CollisionBlock 的定义:

struct CollisionBlock {
  PolyF poly;
  // Will never be None
  CollisionKind kind;
  // Normalzied vector encoding the slope of the block we collided with.
  // Always faces right, y component can be positive or negative.
  Vec2F slope;
};

我可以在 Linux 32 位(g++ 版本 4.7.0 和 4.6.3)、MacOSX(不确定字长和 g++ 版本)、Windows 7 64 位(g++ 版本 4.6.3)、Windows 7 32-位(g++ 版本 4.6.2 和 4.6.3),但不是 Linux 64 位(g++ 版本 4.6.1)。

我使用的是 C++11,而不是 boost。

Poly::center()

Coord center() const {
  return sum(m_vertexes) / (DataType)m_vertexes.size();
}

sum

template<typename Container>
typename Container::value_type sum(Container const& cont) {
  return reduce(cont, std::plus<typename Container::value_type>());
}

reduce

// Somewhat nicer form of std::accumulate
template<typename Container, typename Function>
typename Container::value_type reduce(Container const& l, Function f) {
  typename Container::const_iterator i = l.begin();
  typename Container::value_type res{};

  if (i == l.end())
    return res;

  res = *i++;
  while (i != l.end())
    res = f(res, *i++);
  return res;
}       

sort

template<typename Container, typename Compare>
void sort(Container& c, Compare comp) {
  std::sort(c.begin(), c.end(), comp);
}

这个问题有很多问题。抱歉,我会尝试制作一个较小的测试用例。

更新:

Poly::center 中对sum 的调用替换为std::accumulate 没有帮助。

【问题讨论】:

  • PolyF::center 是什么样的?它对 const 对象和非 const 对象的操作是否不同?它是否尝试将结果缓存在对象中?
  • 将相关代码添加到帖子中。
  • 很明显你打电话的sort不是std::sort,那是什么?
  • 比较器是否可能存在浮点精度问题?如果由于舍入错误,比较器不是严格的弱顺序(比如说它报告 asortCenter 的距离几乎完全相等,则可能会发生这种情况
  • 甚至更多,如果您在reduce 中对res 的默认初始化与对其进行零初始化不同。我以前遇到过这个问题,这可能取决于您对Vec2f 的实现

标签: c++ lambda c++11 g++ segmentation-fault


【解决方案1】:

我相当肯定错误不在您发布的代码范围内。您可以在下面使用我的虚拟代码并慢慢将其变形为您的真实代码,直到它开始出现问题。如果你找到了请告诉我们,我很好奇。

仅供参考,我必须在几个地方编写代码才能获得接近你正在做的事情。所有这一切实际上就是锻炼您发布的一小段代码。

#include <list>
#include <deque>
#include <vector>
#include <algorithm>

typedef double mocish;

typedef int CollisionKind; //is actually enum class
typedef mocish RectF;

class Vec2F {
public:
  Vec2F() {
    vertexes.push_back(0);
    vertexes.push_back(0);
  }
  Vec2F(float a, float b) {
    vertexes.push_back(a);
    vertexes.push_back(b);
  }

  float operator[](unsigned index) const {
    return vertexes[index];
  }

  float operator[](unsigned index) {
    return vertexes[index];
  }

  Vec2F operator+(Vec2F const& other) const {
    return Vec2F(vertexes[0]+other[0], vertexes[1]+other[1]);
  }

  Vec2F operator-(Vec2F const& other) const {
    return Vec2F(vertexes[0]-other[0], vertexes[1]-other[1]);
  }

  Vec2F operator*(float other) const {
    return Vec2F(vertexes[0]*other, vertexes[1]*other);
  }

  Vec2F operator/(float other) const {
    return Vec2F(vertexes[0]/other, vertexes[1]/other);
  }

  Vec2F operator=(Vec2F const& other) {
    vertexes[0] = other[0];
    vertexes[1] = other[1];
    return *this;
  }

private:
  std::deque<float> vertexes;
};

float magSquared(Vec2F const& a) {
  return a[0]*a[0]+a[1]*a[1];
}

typedef Vec2F Coord;

// Somewhat nicer form of std::accumulate
template<typename Container, typename Function>
typename Container::value_type reduce(Container const& l, Function f) {
  typename Container::const_iterator i = l.begin();
  typename Container::value_type res{};

  if (i == l.end())
    return res;

  res = *i++;
  while (i != l.end())
    res = f(res, *i++);
  return res;
}     


template<typename Container>
typename Container::value_type sum(Container const& cont) {
  return reduce(cont, std::plus<typename Container::value_type>());
}


struct PolyF
{
    PolyF()
    {
        m_vertexes.resize(4);
        std::generate( m_vertexes.begin(), m_vertexes.end(), [](){ return Vec2F(std::rand(), std::rand());} );
    }

    std::vector<Coord> m_vertexes;

    Coord center() const 
    {
      return sum(m_vertexes) / (float)m_vertexes.size();
    }
};

struct CollisionBlock 
{
  PolyF poly;
  // Will never be None
  CollisionKind kind;
  // Normalzied vector encoding the slope of the block we collided with.
  // Always faces right, y component can be positive or negative.
  Vec2F slope;
};


template<typename Container, typename Compare>
void sort(Container& c, Compare comp) {
  std::sort(c.begin(), c.end(), comp);
} 

struct CollisionGen
{
    std::deque<CollisionBlock> getPolys( RectF const& ) const
    {
        std::deque<CollisionBlock> collision_block_moc(50);
        return collision_block_moc;
    }
};

struct WorldClient
{
    CollisionGen m_collisionGenerator;


    std::deque<CollisionBlock> getCollisionBlocks(RectF const& boundBox, bool doSort, Vec2F sortCenter) const 
    {
      auto res = m_collisionGenerator.getPolys(boundBox);

    //auto test = magSquared(sortCenter - res.front().poly.center()) < magSquared(sortCenter - res.front().poly.center());

      if (doSort) {
        sort(res, [=](CollisionBlock const& block1, CollisionBlock const& block2) {
        return magSquared(sortCenter - block1.poly.center()) < magSquared(sortCenter - block2.poly.center());
          });
      }

      return res;
    }
};

    int main() 
    {
        WorldClient wc;
        while (true) {
          wc.getCollisionBlocks( 42.0, true, {0,0} );
        }
    }

【讨论】:

  • 我已经编辑了您的帖子并添加了一些内容,以使其能够编译和工作,就像有问题的代码一样。但是,它并没有表现出我在真实代码中遇到的症状。
  • 找到了吗?那是什么?
  • 其他地方的内存损坏。它只是碰巧只在 32 位上显示,而且只有在我使用 const 时才显示。奇怪的。 valgrind 也没有检测到它。双重怪异。
【解决方案2】:

我的猜测是模板 List 上的迭代器不是随机访问迭代器,这是 std::sort 需要的,请注意 std::list 仅提供双向迭代。

如果您已经实现了自己的列表,我建议您仔细检查您的迭代器实现,以确保它是正确的,并提供了随机访问迭代器概念的完整和正确的实现。

【讨论】:

  • 抱歉,List 实际上是我们代码库中std::deque 的包装。不像预期的那样在std::list附近。
【解决方案3】:

我注意到您通过说 [=] 但使用引用将值传递给 lambda。您应该更改它,或者使用 std::ref 传递值以真正使用对实例本身的预期引用。

【讨论】:

  • 我确实注意到了,但是 lambda 的 [=] 部分指的是未传递给函数的值(在这种情况下,它在那里,所以我可以使用 sortCenter)。此外,当我指定 [&amp;] 或仅按值或引用指定 sortCenter 时,它不起作用。
【解决方案4】:

您的比较器是严格的弱顺序吗?它是否始终如一地对其输入进行排序?我怀疑您实际上并没有在reduce 中对res 进行零初始化,因此center() 最终会返回垃圾。一般情况下,这应该很容易检查:在 res 声明之后添加 assert(f(res,res) == res)

如果比较器不一致,使用std::sort 会遇到未定义的行为,这很容易导致崩溃。并对平台、优化等敏感。

【讨论】:

    猜你喜欢
    • 2016-01-23
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多