限制成员函数上的限定符(限制此指针)



注意:澄清一下,问题不在于一般使用 restrict 关键字,而是具体而言将其应用于此处所述的成员函数。

gcc 允许您在成员函数上使用 __restrict__(GNU++ 相当于 C99 的 restrict(限定符,有效地使this成为函数范围内的限制限定指针。牛肉在哪里?

大多数成员函数在其他成员上工作,通过this访问它们,这是一个T* const(通常没有别名(。为了使this可能被别名化,需要在成员函数中以某种方式使用第二个指向类型的指针,并且它必须来自某个地方。
这种情况在非成员函数中经常出现,例如所有二元运算符或任何其他接受至少两个指针或相同、非平凡类型的引用的自由函数。但是,这些函数没有this,因此它们不相关。

赋值运算符、复制构造函数和一元比较运算符是成员函数的示例,其中this原则上可以别名(因为另一个对象是通过引用传递的(。因此,只有为这些函数分配一个限制限定符才有意义 - 编译器应该已经很明显,所有其他函数都具有 restrict 属性(因为永远不会有第二个指向 T 的指针(。

现在,例如,如果您在operator=上使用restrict,则根本不应该检查自分配,因为您说this在该函数的范围内没有别名(如果这是真的,则不会发生自分配(。
显然,这是你不可能提前知道的事情,也是没有意义的事情。

那么,在什么情况下,人们实际上想要给成员函数一个限制限定符,以及它在哪里有意义?

要么我错过了什么,要么你的问题没有意义。 this与成员函数的任何其他参数没有什么不同,那么为什么你对GCC允许你应用restrict感到惊讶呢?

关于将其应用于赋值运算符,您正确地指出,它将避免对明确的自我赋值测试的需求。 然后你说:

显然,这是你不可能提前知道的事情

但是,当您将restrict用于任何事情时,这总是正确的。 例如,有人可能决定调用具有重叠内存区域的memcpy;你"不可能提前知道"他们不会这样做。 但是memcpy论点的restrict声明意味着如果他们这样做,他们就犯了错误。 以完全相同的方式,如果您将赋值运算符声明为 restrict ,则某人自行分配该类的对象是错误的。 这完全没有神秘或矛盾;它只是restrict语义的一部分,它对代码的其余部分施加了某些约束。

我也不确定为什么你发现成员函数不可能将指针(或引用(带到另一个相同类型的对象。 简单的例子:

class Point {
public:
    double distance(const Point &other) const;
};

这种事情总是突然出现。

所以真正的问题是,为什么你认为this与其他任何论点如此不同? 或者如果你愿意,我怎么会这么完全错过你的观点?

我相信

你们缺少的是成员函数的参数也可以别名部分或对象。下面是一个示例

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,因此它必须假设最坏的情况,无论它是聪明还是愚蠢。因此,即使应用于此指针,也存在限制的明确且非常真实的优势。

您发布的链接很有趣。不会有一个可靠的用例来应用restrict this。正如您在问题中提到的,复制构造函数,运算符 = 可能是潜在的候选者;但是编译器可以照顾它们。

但是以下案例可能会很有趣

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;

虽然这是非常不寻常的做法,但这是我能想到的唯一用例。

怕我不清楚你为什么要说this.

restrict指定指针不与其他指针重叠。因此,编译器可以假定限制指针指向的内存区域不依赖,这允许更积极的优化。 __restrict__用于其他指针变量时会比this更有效。

那么,一个人实际上想要给出一个 成员 A 限制限定符以及它在哪里有意义?

回想一下在memcpy中使用restrict指针的代表性案例:

void Foo::MyCompute(__restrict__ char* bufA, __restrict__ char* BufB)
{
}

添加它作为答案,因为它可能更适合这样(这是一种答案,并不真正属于问题,而且评论有点长(。

在思考了尼莫的答案很久之后,我相信我们对自我分配的两种解释可能有些错误(尽管尼莫的解释比我的更正确(。正如 Nemo 正确指出的那样,具有限制限定的this实际上意味着混叠的存在是程序错误。不多也不少。

到目前为止,在写这篇文章时,你的逻辑实际上不应该是"既然你说这不可能发生,你也不应该检查自分配",而是"既然你明确说混叠不会发生,如果发生,那就是程序错误,你不仅需要检查自赋值,而且如果发生,你必然失败"。

而且,到目前为止,它强调特定的程序逻辑,同时允许编译器针对该特定特殊情况进行更好的优化,因此确实有意义

最新更新