与我以前的问题相关:Are compilers not allowed to assume const-ref parameters will stay const?
我的新问题是:是否有特定于编译器的非标准扩展或语法告诉GCC / Clang / MSVC对象不可写?例如,这里有一些我想写的假代码:
void f(const int& i) {
// At this point, compiler doesn't know if "i" can be mutated or not,
// so it assumes it can
// Fake-ish -- compiler now assumes "i" cannot be mutated and optimizes accordingly
__assume(readonly i);
// ...
}
答案 0 :(得分:4)
如果i
应该保留整个函数的常量,并且f()
没有副作用,则可以使用__attribute__((pure))
声明它:
int f(const int&) __attribute__((pure));
请注意,pure
函数返回void
没有意义,因此我将其更改为int
。
虽然这不会影响f()
的编译方式,但它会影响调用它的函数(在godbolt上查看):
#include <iostream>
int f(const int& i) __attribute__((pure));
int main() {
int i = 40;
f(i);
if (i != 40) {
std::cout << "not 40" << std::endl;
}
}
此处__attribute__((pure))
告诉编译器f()
不会更改i
,因此编译器不会生成对std::cout << ...
的调用。
如果没有__attribute__((pure))
,即使声明f()
采用const int& i
参数,编译器也必须假设i
的值可能会发生变化,并生成{ {1}}以及对if
的调用。
答案 1 :(得分:1)
我不知道允许编译器承担不变性的非标准方法,但如果我正确地阅读标准,我认为有一种标准方法可以这样做(改编自你的其他问题):
void f(const int&);
struct ImmutableInt {
// it is UB to change val, because it is itself const
// not just a const reference
const int val;
};
void g3(const ImmutableInt & i) {
if (i.val == 42) f(i.val);
if (i.val == 42) f(i.val); // the compiler could assume i.val has not changed
}
问题是,目前编译器checked没有利用这些知识来实际省略重新加载值。
我认为这没有任何根本问题,因为clang使用类似且更复杂的推理来实现devirtualization虚拟表指针。这种推理有点复杂,因为vptr在构造期间会改变,因此不完全是const。用于此的llvm元数据称为invariant.group,但我不知道您是否可以在C ++中自己设置它。