为lambda表中的VS2010 bug工作?

时间:2011-11-08 22:09:04

标签: c++ visual-studio-2010 lambda

Can I make a table of String + lambdas that have the same signature?中的先前调查显示我在VS2010中实际上可以有一个字符串+ lambdas表。

当lambdas是无效返回类型时,事情看起来很好。但是试图将它们更改为bool返回类型,编译器似乎弄错了,或者存在某种内存损坏错误......在C ++版本中这是不正确的......

以下说明了该方案:

// fun: use a table of lambdas to define what to do in order to update each field
typedef std::function<bool (CDynamicMenuItem *, ITEM *)> LambdaType;
struct UpdateField {
    const TCHAR *   label;
    LambdaType      lambda; // this version allows us to use captures in our lambdas, whereas the following doesn't
    //void (*lambda)(CDynamicMenuItem *, ITEM *);   // this would work in VS11, but the conversion of lambda to function pointer was defined after 2010's release!
};
UpdateField MenuFields[] = {
    { "Identity",   [] (CDynamicMenuItem * pMenuItem, ITEM * pNearestItem) ->bool { return pMenuItem->SetText(FString("%s/%.1f", pNearestItem->thissec->name, pNearestItem->number/10.0)), true; } },
    { "X1",         [] (CDynamicMenuItem * pMenuItem, ITEM * pNearestItem) ->bool { double v = GetX1(pNearestItem); return (v != v) ? false : pMenuItem->SetValue(v), true; } },
    { "Y1",         [] (CDynamicMenuItem * pMenuItem, ITEM * pNearestItem) ->bool { double v = GetY1(pNearestItem); if (v != v) return false; pMenuItem->SetValue(v); return true; } },
    { "X2",         [] (CDynamicMenuItem * pMenuItem, ITEM * pNearestItem) ->bool { double v = GetX2(pNearestItem); if (v != v) return false; pMenuItem->SetValue(v); return true; } },
    { "Y2",         [] (CDynamicMenuItem * pMenuItem, ITEM * pNearestItem) ->bool { double v = GetY2(pNearestItem); if (v != v) return false; pMenuItem->SetValue(v); return true; } },
    { "Xd",         [] (CDynamicMenuItem * pMenuItem, ITEM * pNearestItem) ->bool { double v = GetXd(pNearestItem); if (v != v) return false; pMenuItem->SetValue(v); return true; } },
    { "Yd",         [] (CDynamicMenuItem * pMenuItem, ITEM * pNearestItem) ->bool { double v = GetYd(pNearestItem); if (v != v) return false; pMenuItem->SetValue(v); return true; } },
    { "Angle",      [] (CDynamicMenuItem * pMenuItem, ITEM * pNearestItem) ->bool { double v = GetAngle(pNearestItem); if (v != v) return false; pMenuItem->SetValue(v); return true; } },
    { "Length",     [] (CDynamicMenuItem * pMenuItem, ITEM * pNearestItem) ->bool { double v = GetLength(pNearestItem); if (v != v) return false; pMenuItem->SetValue(v); return true; } },
};

for (UpdateField * it = &MenuFields[0], * end = (MenuFields + countof(MenuFields)); it != end; ++it)
{
    CDynamicMenuItem * pMenuItem = pMenu->FindItem(it->label);
    if (pMenuItem)
    {
        if (!m_pNearestItem || !it->lambda(pMenuItem, m_pNearestItem))
            pMenuItem->SetText("");
    }
}

当lambda的返回类型为void(即 - &gt; bool被省略,并且各种lambda体被修改为不返回任何内容等)时,上述工作非常漂亮。

然而,让我们返回一个bool来指示lambda是否能够处理该字段的数据是否有用,如果没有,则让调用者处理清除该字段。

确定,代码编译&amp;运行...直到它达到此代码&amp;崩溃。在调试器中查看“MenuFields []”会显示大多数MenuField [x] .label地址的垃圾(有时其中一个是正确的,但我还没弄清楚模式是什么)。

我想也许编译器在静态初始化列表中嵌入了lambda的语法,但是我不知道我能做些什么呢?

我尝试了这种变化:

    { "Identity",   LambdaType( [] (CDynamicMenuItem * pMenuItem, ITEM * pNearestItem) ->bool { return pMenuItem->SetText(FString("%s/%.1f", pNearestItem->thissec->name, pNearestItem->number/10.0)), true; } ) },

编译器同样喜欢这样,但它会导致相同的损坏的表数据。

类似地,编译器可以在整个lambda周围放置括号,并且在运行时同样会损坏。

所以,有些问题:

  1. 你看到我忽略了什么吗?
  2. 你能想到一个让编译器生成正确代码的工作(除了回到void返回 - 这对我的场景来说是合理的,而我可能在下一步中提出更好的建议)?
  3. 知道如何最好地向Microsoft报告此事吗? [我没有幸运在另一端找到真正的人类来提供这种详细的信息,以便它实际上在某个地方而不是&gt; nul)

1 个答案:

答案 0 :(得分:3)

这确实是Visual C ++编译器中的一个错误。请参阅错误报告"miscompilation of aggregate initializer with lambdas inside"

作为解决方法,请考虑不在此处使用聚合初始化。相反,您可以使用std::vector

UpdateField MakeUpdateField(char const* label, LambdaType const lambda)
{
    UpdateField f = { label, lambda };
    return f;
}

std::vector<UpdateField> fields = ([]() -> std::vector<UpdateField>
{
    std::vector<UpdateField> data;
    data.push_back(MakeUpdateField("Identity", [] { /* contents omitted */ }));
    data.push_back(MakeUpdateField("X1",       [] { /* contents omitted */ }));
    data.push_back(MakeUpdateField("Y1",       [] { /* contents omitted */ }));
    return data;
})();

(如果您的数据是名称到lambda的映射,您可能还会考虑使用std::map,这可能会提供更好的性能特征,或者可能更容易使用。)