如何在不违反MISRA C ++ 2008咨询规则5-2-10的情况下使用std :: transform?

时间:2013-08-09 10:49:57

标签: c++ misra

我在PC-Lint(au-misra-cpp.lnt)中收到这些错误:

  

ConverterUtil.cpp(90):   错误864 :(信息 - 涉及变量'transformValue'的表达式   可能取决于评估顺序[ MISRA C ++规则5-2-10 ])

     

ConverterUtil.cpp(90):   错误864 :(信息 - 涉及变量'transformValue'的表达式   可能取决于评估顺序[ MISRA C ++规则5-2-10 ])

     

ConverterUtil.cpp(90):   错误534 :(警告 - 忽略功能的返回值   “的std ::变换(标准:: _ St​​ring_iterator>>中   的std :: _ St​​ring_iterator>>中   std :: _ St​​ring_iterator>>,int   (*)(int))'(与第998行,文件C:\ Program Files比较   (x86)\ Microsoft Visual Studio 11.0 \ VC \ include \ algorithm)[MISRA C ++   规则0-1-7和8-4-6],[MISRA C ++规则0-3-2])

关于此代码:

/**Conversion from std::string to bool*/
bool ConverterUtil::ConvertStdStringToBool(const std::string value)
{
    std::string transformValue = value;
    bool retValue = false;

    std::transform(transformValue.begin(), transformValue.end(), transformValue.begin(), &::tolower);


    if(transformValue == std::string(static_cast<const char *>("true")))
    {
        retValue = true;
    }

    return retValue;
}

我猜测它不喜欢我在转换中使用相同的std :: string作为输入和输出的事实,但使用其他字符串作为输出会产生相同的错误。

是否可以使std :: transform MISRA兼容?

3 个答案:

答案 0 :(得分:5)

我只是在这里猜测(如果不能解决你的问题,我可能会删除答案)。

尝试将包含std::transform的行替换为以下两行:

auto dest = transformValue.begin();
std::transform(transformValue.cbegin(), transformValue.cend(), dest, &::tolower);

请注意使用cbegin()cend()(而不是begin()end())。

关于另一个话题:当你只做一次时,你正在复制传递给ConvertStdStringToBool两次的字符串。为此,请替换:

bool ConverterUtil::ConvertStdStringToBool(const std::string value)
{
    std::string transformValue = value;

bool ConverterUtil::ConvertStdStringToBool(std::string transformValue)
{

(您可能希望在此次更改后将transformValue重命名为value

更新:我的解释为什么我认为这会有所帮助。

首先,请注意transformValue为非const。因此,transformValue.begin()transformValue.end()会调用这些重载:

iterator begin(); // non const overload
iterator end();   // non const overload

因此,静态分析器(正确地)得出结论:begin()end()可能会改变transformValue的状态。在这种情况下,transformValue的最终状态可能取决于首先调用begin()end()中的哪一个。

现在,当您致电cbegin()cend()时,重载就是:

const_iterator cbegin() const; // notice the const 
const_iterator cend() const;   // notice the const

在这种情况下,静态分析器不会推断这些调用会改变transformValue的状态并且不会引发问题。 (严格地说,即使这些方法是const,它们也可以改变状态,因为类中可能存在mutable个数据成员,或者方法可能使用邪恶的const_cast。静态分析器恕我直言不应该因此而受到指责。)

最后评论:电话

std::transform(transformValue.cbegin(), transformValue.cend(), transformValue.cbegin(), &::tolower);
                                                                              ^^^^^^

错了。第三个参数必须是非const迭代器,也就是说,必须transformValue.begin()(只有前两个参数是{{1}方法)。

但是,我想,对于上述类似的推理,仅仅使用c*作为第三个参数是不够的,这就是为什么我建议创建另一个变量(transformValue.begin())。

答案 1 :(得分:2)

这不是一个单独的答案,更像是对Cassio答案的评论,但是评论太长了。

以下是直接使用transformValue.begin()作为第三个参数实际上很容易失败的原因:在C ++ 03中(不是在11中,但到目前为止GCC尚未切换),std ::的引用计数实现字符串被允许。 libstdc ++有一个。有了这样的版本,valuetransformValue将共享其内部缓冲区。

现在,当调用transformValue.begin()时,生成的非const迭代器可以用来修改缓冲区,这会很糟糕,因为它也会改变value。所以begin()必须取消共享缓冲区,即仅为transformValue分配一个唯一的缓冲区。这样做使所有现有的迭代器无效

因此,在调用cbegincend的C ++ 98版本中:

const std::string& constValue = transformValue;
std::transform(constValue.begin(), constValue.end(),
               transformValue.begin(), &::tolower);

你有一个真正的订单依赖。如果在const调用之前调用非{const}版本的begin(),那么一切都很好。但是如果首先调用const版本(或end()),那么新返回的迭代器将被非const调用无效,从而产生未定义的行为。

足够多,代码仍然可能正常工作,因为无效的迭代器将指向旧缓冲区,由value保持活动状态。因为在大多数情况下发生这种疏散,旧版本将比新版本更长,这是一个非常讨厌的睡眠错误,直到任何一个都不会出现

  • 旧的副本可能会在操作过程中消失,例如因为旧副本已经消失,但另一个副本上存在另一个副本,并且可能随时消失,或者
  • 代码的改变方式是希望通过非const迭代器进行的更改反映在const迭代器中(指向不同的缓冲区,因此不会看到它们)。

答案 2 :(得分:0)

虽然迭代器begin()和end()在此调用中是常量,但检查器怀疑调用这些副作用。因此,结果可能会有所不同,具体取决于调用顺序。我有类似的问题,不得不通过使用两个局部变量来关闭检查器来修复它。