注意:为了澄清,问题不在于一般情况下restrict
关键字的使用,而是关于如here所述将其应用于成员函数。< / p>
gcc允许您在成员函数上使用__restrict__
(GNU ++等效于C99的restrict
)限定符,有效地使this
成为函数范围内的限制限定指针。牛肉在哪里?
大多数成员函数都在其他成员上工作,通过this
访问它们,这是一个T* const
(并且通常是无法使用的)。对于this
可能存在别名,需要在成员函数中以某种方式使用第二个指向类型的指针,并且它必须来自某个地方。
这种情况通常是非成员函数的情况,例如所有二元运算符或任何其他自由函数,其取至少两个指针或相同的非平凡类型的引用。但是,这些函数没有this
,因此它们不相关。
赋值运算符,复制构造函数和一元比较运算符是成员函数的示例,其中this
原则上可以为别名(因为另一个对象通过引用传递)。因此,为这些分配限制限定符才真正有意义 - 编译器应该已经明白所有其他函数都具有restrict属性(因为从来没有第二个指针指向T)。
现在,如果您在restrict
上使用了operator=
,那么您应该因此根本不检查自我分配,因为您说的是{{1}在该函数的范围内没有别名(并且如果那是真的,则不会发生自我赋值)。
显然,这是你事先不可能知道的事情,也是没有意义的事情。
那么,如果一个人真的想给成员函数一个限制限定符并且它有意义呢?
答案 0 :(得分:3)
要么我缺少某些东西,要么你的问题没有意义。 this
不是 与成员函数的任何其他参数不同,那么为什么GCC允许您对其应用restrict
感到惊讶?
关于将它应用于赋值运算符,您正确地指出它将消除对显式自赋值测试的需要。然后你说:
显然,这是你事先不可能知道的事情
但是当您使用restrict
进行任何操作时,总是为真。例如,某人可能决定使用重叠的内存区域调用memcpy
;你“不可能提前知道”他们不会这样做。但是restrict
参数的memcpy
声明意味着如果它们已经提交了错误。以完全相同的方式,如果声明赋值运算符restrict
,则表示某人自行分配该类的对象是错误的。这根本没有什么神秘或矛盾的;它只是restrict
语义的一部分,它对你的其余代码施加了某些限制。
我也不确定为什么你发现成员函数不可能将指针(或引用)带到同一类型的另一个对象上。琐碎的例子:
class Point {
public:
double distance(const Point &other) const;
};
这种事情一直在增加。
所以真正的问题是,为什么你认为this
与其他论点如此不同?或者如果你愿意,我怎么完全错过了你的观点?
答案 1 :(得分:2)
我相信你们缺少的是一个成员函数的参数也可以别名部分或一个对象。这是一个例子
struct some_class {
int some_value;
void compute_something(int& result) {
result = 2*some_value;
...
result -= some_value;
}
}
人们可能希望编译到
*(this + offsetof(some_value)) -> register1
2*register1 -> register2
...
register2 - register1 -> result
如果有人将some_value的引用传递给结果,那么该代码将错误。因此,编译器实际上需要生成以下
*(this + offsetof(some_value)) -> register1
2*register1 -> register2
register2 -> result
...
*(this + offsetof(some_value)) -> register1
result -> register2
register2 - register1 -> register2
register2 -> result
显然效率低下。请注意,除非compute_something是内联的,否则编译器有无方式知道结果是否可能为some_value设置别名,因此 以假设最坏的情况,无论是否聪明或这是愚蠢的。因此,即使应用于此指针,限制也有明确且非常明显的优势。
答案 2 :(得分:1)
您发布的链接很有趣。在restrict
上应用this
将没有可靠的用例。正如您在问题中提到的那样,复制构造函数,operator = 可能是潜在的候选者;但是编译器可以处理它们。
但以下案例可能很有意思
struct A
{
//...
void Destroy (A*& p) __restrict__
{
delete this;
p = 0;
p++;
}
};
现在用例可以;
A **pp = new A*[10];
for(int i = 0; i < 10; i++)
pp[i] = new A;
//...
A* p = pp[0];
for(int i = 0; i < 10; i++)
p->Destroy(p);
delete[] pp;
虽然这是非常不寻常的做法,但这是我能想到的唯一一个用例。
答案 3 :(得分:0)
我担心我不明白为什么你在谈论this
。
restrict
指定指针不与其他指针重叠。因此,编译器可以假设限制指针指向的内存区域不依赖,这允许更积极的优化。当用于__restrict__
之外的其他指针变量时,this
会更有效。
那么,实际上想要给出的是什么情况呢 成员限制限定符和有意义的地方?
请回忆一下在restrict
中使用memcpy
指针的代表性案例:
void Foo::MyCompute(__restrict__ char* bufA, __restrict__ char* BufB)
{
}
答案 4 :(得分:0)
添加这个作为答案,因为它可能更适合这样(它是一种答案,并不真正属于问题,加上评论有点长)。
在长时间考虑Nemo的答案之后,我相信我们对自我任命的两种解释可能都有些错误(尽管Nemo比我的更正确)。正如Nemo正确指出的那样,拥有一个限制合格的this
实际上意味着存在别名是一个程序错误。不多也不少。
就此而言,在写这篇文章时,你的逻辑实际上应该不是“既然你说这不可能发生,你就不应该检查自我分配”,而是“因为你明确地说别名不能发生,而且它是一个程序错误如果确实如此,不仅需要检查自我分配,但是如果发生这种情况,必须必须失败“。
而且,就此而言,它强调特定的程序逻辑,同时允许编译器针对特定的特殊情况进行更好的优化,因此确实有意义。