为什么编译器不优化此加载 下面的C++程序: struct O { const int id; }; extern void externalFunc(); int func(O* o) { //first load of o->id happens here: int first = o->id; externalFunc(); //second load happens here, but why? return o->id + first; }

为什么编译器不优化此加载 下面的C++程序: struct O { const int id; }; extern void externalFunc(); int func(O* o) { //first load of o->id happens here: int first = o->id; externalFunc(); //second load happens here, but why? return o->id + first; },c++,C++,Clang和MSVC都对该代码进行了所有优化,以从内存中加载两次o->id值的方式编译该代码 为什么这些编译器无法删除第二个加载?我试图通过将id成员标记为const来告诉编译器该值保证不会更改,但显然两个编译器都没有找到足够的保证。如果我删除externalFunc()的调用,它们会优化掉第二个加载。我如何说服编译器这个值真的不会改变?externalFunc()可能会改变o->id。(不是局部变量)考虑: #include <iostream> struct O { co

Clang和MSVC都对该代码进行了所有优化,以从内存中加载两次o->id值的方式编译该代码

为什么这些编译器无法删除第二个加载?我试图通过将id成员标记为const来告诉编译器该值保证不会更改,但显然两个编译器都没有找到足够的保证。如果我删除externalFunc()的调用,它们会优化掉第二个加载。我如何说服编译器这个值真的不会改变?

externalFunc()
可能会改变
o->id
。(不是局部变量)考虑:

#include <iostream>

struct O
{
   const int id;
   O(int x) : id(x) {}
};

O* global = nullptr;

void externalFunc() {
    global->~O();
    new(global) O(42);
}

int func(O* o)
{
    int first = o->id;
    externalFunc();
    // o->id has changed, even though o hasn't    
    return o->id + first;
}

int main()
{
    O o(1);
    global = &o;
    std::cout << func(&o);
}
#包括
结构O
{
const int id;
O(intx):id(x){}
};
O*global=nullptr;
void externalFunc(){
全局->~O();
新的(全球)O(42);
}
int func(O*O)
{
int first=o->id;
externalFunc();
//o->id已更改,即使o未更改
首先返回o->id+;
}
int main()
{
O(1);
全局=&o;
标准::cout
为什么这些编译器无法删除第二个加载?我试图通过将id成员标记为const来告诉编译器该值保证不会更改,但显然这两个编译器都没有找到足够的保证

因为它不是。考虑这个例子。

static O mg {5};

void
externalFunc()
{
  mg.~O();
  new (&mg) O {6};
}

int
main()
{
  std::cout << mg.id << '\n';
  func(&mg);
  std::cout << mg.id << '\n';
}

我已经养成了一种习惯,将通过指针(包括
this
指针)访问的基本字段的所有值缓存到本地(
const
)变量。如果编译器可以确保值不能更改,则不会产生额外的成本,如果不能更改,则可能会生成更好的代码。顺便说一句,它还允许您为函数上下文中最有意义的值命名。

编译器本身没有
externalFunc()的代码
在编译
func()
时,它不知道自己会做什么。因此,它起到了屏障的作用

如果静态链接,这将属于链接时间优化(可以在GCC上通过
-flto
启用),MSVC也支持

为了向GCC/Clang(看起来MSVC不支持此函数)提示该函数不会更改全局内存,您可以使用
pure
属性对其进行标记:

extern void __attribute__((pure)) void externalFunc();

然后它将停止设置障碍。

externalFunc
可以在
O
指向的内存中销毁并重新创建
O
对象。如果
func()
使用
常量O&O
常量O*常量O
?演示
*O
func
下更改:尝试
int func(O*\u restrict O){
它怎么可能?如果尝试O->id=123,id是常量;它无法编译它可能“放置新的位置”位于该位置的对象。
const
实际上并不保证它不会被写入。您可以通过const_cast添加/删除constness。这通常是一个坏主意,但仍然有效,编译器必须在任何情况下生成正确的代码。@RamboRamon:不正确,在最初为const的变量上丢弃constness是不正确的定义的行为。因此允许编译器忽略这种可能性。@Bathsheba我可能有点太简短了。我想到了类似
const int*p=&(o->id);*(const_cast(p))的东西=5;
如果这仍然是未定义的行为,那么我学到了一些新的东西。thnx!。你知道如何向编译器保证这不会发生吗?我想是通过值传递的。这个练习的最终目标是什么?这个问题听起来很可疑。
extern void __attribute__((pure)) void externalFunc();