【问题标题】:Does this C++ static analysis rule make sense as is?这个 C++ 静态分析规则是否有意义?
【发布时间】:2012-07-17 07:37:26
【问题描述】:

我正在实现一些 C++ 静态分析规则,其中一个禁止函数返回引用或指向函数引用参数的指针,即以下均不符合:

int *f(int& x) { return &x; } // #1
const int *g(const int& x) { return &x; } // #2
int& h(int& x) { return x; } // #3
const int& m(const int& x) { return x; } // #4

为此给出的理由是“无论引用参数是临时对象还是对参数的引用,都是实现定义的行为。”

不过,我对此感到困惑,因为 C++ 中的流运算符是这样编写的,例如

std::ostream& operator<<(std::ostream& os, const X& x) {
    //...
    return os;
}

我认为我非常有信心 C++ 中的流运算符通常不会表现出实现定义的行为,那么这是怎么回事?

根据我目前的理解,我希望 #1 和 #3 是明确定义的,基于临时对象不能绑定到非常量引用,所以 int&amp; x 指的是一个真实的对象它的生命周期超出了函数的范围,因此返回对该对象的指针或引用就可以了。我希望#2 是狡猾的,因为临时可能已绑定到const int&amp; x,在这种情况下,尝试获取其地址似乎是一个糟糕的计划。我不确定#4 - 我的直觉是这也可能是狡猾的,但我不确定。特别是,我不清楚在以下情况下会发生什么:

const int& m(const int& x) { return x; }
//...
const int& r = m(23);

【问题讨论】:

  • @Nawaz:我正在使用 .QL 在大型代码库上编写查询 :) 我认为编译器无关紧要,理想情况下,我正在寻找一个独立于平台的答案。
  • 为什么我问是因为 MSVC++ 提供编译器扩展,允许临时绑定到非常量引用。如果你使用的是微软静态分析工具,那么它也可能会考虑这个扩展。
  • @Nawaz:啊,对 :) 在这种情况下,我可能应该为 VC++ 的规则提供一个可选扩展。谢谢! (我不得不说我一般不喜欢这样的编译器扩展。)
  • 我没有使用 Microsoft 静态分析工具,它是内部工具 - 但听起来我需要在其中添加一些 Microsoft 特定的东西。
  • 您可以通过添加 /Za(禁用语言扩展) 标志来禁用绑定。

标签: c++ reference language-lawyer reference-binding


【解决方案1】:

正如你所说,#1 和 #3 很好(尽管 #1 可以说是不好的风格)。

#4 很狡猾,原因与 #2 相同;它允许将 const 引用传播到其生命周期之后的临时对象。

让我们检查一下:

#include <iostream>

struct C {
  C() { std::cout << "C()\n"; }
  ~C() { std::cout << "~C()\n"; }
  C(const C &) { std::cout << "C(const C &)\n"; }
};

const C &foo(const C &c) { return c; }

int main() { 
   const C &c = foo(C());
   std::cout << "c in scope\n";
}

这个输出:

C()
~C()
c in scope

【讨论】:

  • 谢谢 - 我不确定临时的生命周期是否会因为做这种事情而延长。似乎答案是坚定的“不”,ta。
  • 那么流操作符呢?
  • @Arne:他们接受并返回一个非常量引用,所以他们很好。
【解决方案2】:

在 C++11 中,如果还存在右值引用重载,则 #2 和 #4 可以变得安全。因此:

const int *get( const int &x ) { return &x; }
const int *get( const int &&x ) { return nullptr; }

void test() {
    const int x = 0;
    const int *p1 = get( x ); // OK; p1 is &x.
    const int *p2 = get( x+42 ); // OK; p2 is nullptr.
}

因此,尽管它们很狡猾,但如果程序员知道它们在做什么,它们确实可以安全使用。禁止这样做是很严厉的。

(如果将 const rvalue 引用重载设为私有、未定义或以其他方式导致编译时或链接时错误,则可能更安全。对于 #4 情况尤其如此,我们返回引用但没有什么好返回引用,并且语言不允许引用 null。)

【讨论】:

  • 谢谢,好点,尤其是未来。这样做的背景是,我正在为我们的商业工具实施一套相当严格的规则——它们被一个标准所限制,所以我必须小心偏离规则的文字太远。也就是说,目的是尽可能减少误报。我们还不支持 C++11,但当我们支持时,我肯定会倾向于修改规则以将右值引用重载考虑在内。
  • 返回对右值参数的引用是“安全”的一种情况是std::movestd::forward,它们都可能采用右值参数。 IOW,如果您打算将此类函数的结果直接传递给另一个表达式,无论它可能是什么(如另一个函数)。
猜你喜欢
  • 2014-05-30
  • 2012-08-01
  • 2023-04-02
  • 2013-09-20
  • 1970-01-01
  • 2011-01-10
  • 2011-08-27
  • 2019-09-27
  • 1970-01-01
相关资源
最近更新 更多