1

我在 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::transform(std::_String_iterator>>, std::_String_iterator>>, std::_String_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 兼容?

4

3 回答 3

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)
{

(您可能希望在此更改后重命名transformValuevalue)。

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

首先,注意那transformValue是 non const。因此,transformValue.begin()并将transformValue.end()调用这些重载:

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

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

现在,当您调用cbegin()and时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()(只有前两个参数是c*方法)。

但是,我想,出于与上述类似的原因,仅将transformValue.begin()其用作第三个参数是不够的,这就是我建议创建另一个变量 ( dest) 的原因。

于 2013-08-09T10:55:57.753 回答
2

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

以下是直接transformValue.begin()用作第三个参数实际上很容易失败的方式:在 C++03 中(不是在 11 中,但 GCC 到目前为止还没有切换),允许 std::string 的引用计数实现。libstdc++ 有一个。有了这样的版本,value就会transformValue共享它们的内部缓冲区。

现在,当transformValue.begin()被调用时,生成的非常量迭代器可用于修改缓冲区,这很糟糕,因为它也会改变value。所以begin()必须取消共享缓冲区,即为transformValue. 这样做会使所有现有的迭代器失效!

因此,在调用cbeginand的 C++98 版本中cend

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

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

令人讨厌的是,代码可能仍然可以工作,因为无效的迭代器将指向旧缓冲区,该缓冲区由value. 由于在发生这种取消共享的大多数情况下,旧副本将比新副本长,这是一个非常讨厌的休眠错误,直到任何一个都不会出现

  • 旧副本可能会在操作过程中消失,例如因为旧副本已经消失,但另一个副本存在于另一个线程上并且可能随时消失,或者
  • 代码的更改方式期望通过非常量迭代器所做的更改反映在常量迭代器中(指向不同的缓冲区,因此不会看到它们)。
于 2013-08-09T12:14:40.490 回答
0

尽管迭代器 begin() 和 end() 在此调用中是常量,但检查器怀疑调用它们会产生副作用。因此,根据调用顺序,结果可能会有所不同。我有一个类似的问题,不得不通过使用两个局部变量来关闭检查器来修复它。

于 2016-07-02T14:00:19.707 回答