构造函数是否会影响封闭对象的其他字段,或者这是静态分析误报?

Jos*_*ica 6 c++ constructor clang clang-static-analyzer

考虑这个 C++ 代码:

struct SomeStruct {
  SomeStruct() noexcept;
};

//SomeStruct::SomeStruct() noexcept {}

class SomeClass {
  const bool b;
  const SomeStruct s;

public:
  SomeClass() : b(true) {}
  operator bool() const { return b; }
};

void f() {
  int *p = new int;
  if (SomeClass())
    delete p;
}
Run Code Online (Sandbox Code Playgroud)

当我运行clang --analyze -Xanalyzer -analyzer-output=text它时,我得到这个

q72007867.cpp:20:1: warning: Potential leak of memory pointed to by 'p' [cplusplus.NewDeleteLeaks]
}
^
q72007867.cpp:17:12: note: Memory is allocated
  int *p = new int;
           ^~~~~~~
q72007867.cpp:18:7: note: Assuming the condition is false
  if (SomeClass())
      ^~~~~~~~~~~
q72007867.cpp:18:3: note: Taking false branch
  if (SomeClass())
  ^
q72007867.cpp:20:1: note: Potential leak of memory pointed to by 'p'
}
^
1 warning generated.
Run Code Online (Sandbox Code Playgroud)

不过,取消注释 的构造函数的定义SomeStruct会使警告消失。const bool b;交换和的顺序const SomeStruct s;也会使其消失。在原始程序中,实际上是否存在 的构造函数的其他定义SomeStruct会导致在那里采取错误分支,或者这是 Clang 静态分析器中的误报?

Yak*_*ont 5

没有标准兼容的方法可以const在初始化后更改成员;任何机制都将是UB。

喜欢

struct foo{
  const bool b=true;
  foo(){ b=false; }
};
Run Code Online (Sandbox Code Playgroud)

是非法的,编辑它的代码也是const_cast非法的:b

struct foo{
  const bool b=true;
  foo(){ const_cast<bool&>(b)=false; }
};
Run Code Online (Sandbox Code Playgroud)

(第二个版本可以编译,但会生成 UB)。

遗憾的是,这样的 UB 并不罕见。例如,我可以实现 的构造函数来在指针地址SomeStruct之前修改内存this。这将是双重非法的(const在构造后修改值,并违反可达性规则),但根据优化设置,它可以工作。

另一方面,编译器可以自由地注意到唯一的构造函数分配trueb然后转换operator bool为仅 return true

b但相反,一旦发生对可见源代码之外的函数体的调用,静态代码分析器就会放弃验证状态。放弃这是一件非常合理的事情。在这里,该函数甚至获取指向同一个临时对象的指针;做一个完整的证明,无论运行什么代码,指针都不能改变某些状态是可能的,但不这样做似乎也是合理的。

就风格而言,代码也有点混乱。一个可证明为真的分支要么不应该存在,要么失败的分支在语义上应该有意义。两者都没有发生在这里;任何阅读此代码的人都无法从代码结构确定正确性;代码结构看起来有误导性。