如果函数参数被注释为const int &x
,并且我试图在函数体中执行x++
,则在修改只读引用时会出现编译时错误。但如果我使用__restrict__
修饰符,比如:
void foo(int & __restrict__ a, int & __restrict__ b) {
if (a == 1)
b = 2;
if (a == 2)
b = 3;
}
int main() {
int x = 1;
foo(x, x); // should be illegal?
cout << x;
}
我没有得到编译时错误。如果我在未优化的情况下运行此代码,则输出为3
,但如果我使用-O1
或更高版本运行它,则输出是2
。检测到x
被传递两次似乎很简单,而且很容易被拒绝。为什么C++可以防止const
的不良使用,而不能防止__restrict__
的不良使用?
您正在向后看__restrict__
__restrict__
是一个实现扩展,程序员可以使用它来发出意图,以最大限度地提高生成的代码质量和性能("优化"(。
它不是检查,也不是对程序添加的约束。
它不是类型系统的一部分,因此也不是函数类型的一部分。因此(通常(不能在调用站点强制执行它。
它就像其他一些扩展(如__builtin_unreachable
(一样,使用它们来告诉编译器一些事情。
在这种情况下,您告诉它"我不是通过任何其他指针来指代指针对象"。
你并不是在问它"请阻止我通过任何其他指针引用指针对象"。
C和C++编译器已经在可能的地方执行了强大的"混叠"检查。__restrict__
关键字是您在自动混叠检测无法工作(例如,由于翻译单元边界(的情况下告诉它"我对此很确定"的一种方式。由于自动混叠检测无法工作,因此无法强制执行__restrict__
。
而且,即使可以,这也与修饰语的目的相反。
在一般情况下,不可能在编译时对其进行诊断。作为一个微不足道的反例,考虑一下:
void foo(int* __restrict__ a, int * __restrict__ b);
int x;
int y;
std::cin >> x >> y;
int* a = (x%2) ? &x : &y;
int* b = (y%2) ? &x : &y;
foo(a,b);
编译器无法知道a
和b
是否会指向同一个int
。实际上,如果编译器可以进行这样的分析,就不需要__restrict__
限定符,因为这样编译器就可以自己判断是否使用两个指针来访问同一内存。
通常检测restrict
违规并不容易。假设你有一个函数
void bar(int* p1, int* p2) {
foo(*p1, *p2);
}
在这种情况下,编译器应该做什么?
在某些情况下(如您的示例(,restrict
冲突可以被检测到,并且会被某些编译器检测到。例如,带有-Wrestrict
的GCC会产生警告:
警告:传递参数1以限制具有参数2 的合格参数别名
-Wrestrict
上的GCC文档读取:
当
restrict
限定参数(或者在C++中,__restrict
限定参数(引用的对象被另一个参数别名时,或者当这些对象之间的副本重叠时,发出警告。
您可以使用with-Werror=restrict
选项将此警告转换为错误。