1

在调试会话之后,我意识到下一个示例代码编译时没有警告(g++-4.7.2 -Wextra 和 -Wall)

char *p;
char *q = std::move(p);
*p = 'p';
*q = 'q';

我有点震惊,对此我有三个问题。

1-我是否遗漏了有关移动语义的内容,并且在这种情况下没有发出警告是有原因的,还是 gcc 的检查不够好?

2-下一个代码应该发出警告吗?

char c = 'c';
char *p = &c;
char *q = std::move(p);
*p = 'p';

据我了解, afterstd::move(p) p可以保存任何值,因此一旦p被移动就像一个未初始化的变量。

3-其他工具/编译器可以检查这些错误吗?

4

2 回答 2

1

1-我遗漏了一些关于移动语义的东西,在这种情况下有理由不发出警告,或者 gcc 检查不够好?

代码是错误的,所以警告会很好。G++ 不够聪明,无法“看穿”对std::move. 通过引用函数(该函数可能会初始化变量)传递未初始化的变量是有效的,因此调用std::move自身不会触发警告。因为q被分配了一个值,所以它似乎被初始化给编译器。

如果我打开优化以便 G++ 内联调用,std::move那么我会收到来自 G++ 4.7 的错误:

f.cc: In function ‘int main()’:
f.cc:6:9: warning: ‘p’ is used uninitialized in this function [-Wuninitialized]

这是因为对编译器的调用std::move不再是“不透明的”,当它分析内联代码时,它可以看到p永远不会被赋值。编译器仍然不够聪明,看不到它q永远不会得到一个好的值,里面的转换std::move可能会让编译器感到困惑。

2-下一个代码应该发出警告吗?

std::move,不会改变指针等基本类型,因此不会改变 的值p。标准库表示,对象在被移动后处于“有效但未指定”状态,因为标准通常不定义移动构造函数或移动赋值运算符的确切行为,但对于基本类型,例如intchar*没有移动构造函数。std::move(p)只是将对象转换为右值,它不会改变它,并且q用值初始化也不会改变它 - 它只是复制值。

3-其他工具/编译器可以检查这些错误吗?

即使启用了优化,Clang 和 ICC 也无法警告第一个示例。

请注意,尽管警告非常有用,但编译器并不完美,不可能警告所有不安全的代码。当不安全的代码没有收到警告时,你不应该感到完全惊讶(也许打开一个错误报告让编译器请求改进)——这并不意味着代码是好的。没有警告并不意味着没有错误。

于 2013-01-19T22:40:00.270 回答
0

指针的移动构造函数是微不足道的,这意味着它与它的复制构造函数相同:复制值。因此,就 C++11 而言,这将复制指针。在您的第一种情况下,它正在复制未初始化的指针。

现在,如果这些是std::unique_ptr对象,那将不是正确的代码。但是没有理由期望编译器会警告它,因为编译器无法确定移出对象不能operator*调用它。

通常,标准库声明已被移出的对象处于有效但未定义的状态。例如,这是完全合法的:

std::vector<int> src = ...;
std::vector<int> dest = std::move(src);
src.assign(...);

std::unique_ptr不同之处在于它准确地指定了移动对象离开它的状态。但是编译器不能假设任何类型。它不能假设一个对象处于任何特定函数调用都必须失败的无效状态。

于 2013-01-19T22:42:44.757 回答