ограничить классификатор для функций-членов (ограничить этот указатель) - PullRequest
4 голосов
/ 24 июля 2011

Примечание: Для пояснения вопрос не об использовании ключевого слова restrict в целом, а о применении его к функциям-членам, как описано здесь .

gcc позволяет вам использовать __restrict__ (GNU ++, эквивалентный restrict) в C99 для функции-члена, что фактически делает this ограниченным квалифицированным указателем в области действия функции.Где говядина?

Большинство функций-членов работают с другими членами, получая к ним доступ через this, то есть T* const (и обычно не сглаженные).Чтобы this мог быть псевдонимом, в функции-члене каким-то образом должен использоваться второй указатель на тип, и он должен откуда-то приходить.
Это ситуация, которая регулярно имеет местос функциями, не являющимися членами, такими как, например, все бинарные операторы или любая другая свободная функция, которая принимает как минимум два указателя или ссылки идентичного нетривиального типа.Однако эти функции не имеют this, поэтому они не имеют отношения.

Оператор присваивания, конструктор копирования и унарные операторы сравнения являются примерами функций-членов, где this может в принципе быть псевдонимом (поскольку другой объект передается по ссылке).Так что на самом деле имеет смысл присваивать им ограничивающий квалификатор - для компилятора уже должно быть очевидно, что все остальные функции в любом случае имеют свойство restrict (потому что никогда не бывает второго указателя на T).

Теперь, если, например, вы использовали restrict на operator=, вы должны последовательно вообще не проверять самопредставление, потому что вы говорите, что this не является псевдонимом в области действия.эта функция (и , если это правда , самоопределение не может произойти).
Очевидно, это то, что вы не можете знать заранее, и это тоже не имеет смысла.

Итак, что было бы в случае, когда кто-то действительно хотел бы дать функции-члену ограничительный квалификатор и где это имеет смысл?

Ответы [ 5 ]

3 голосов
/ 25 июля 2011

Либо я что-то упустил, либо ваш вопрос не имеет смысла.this не , что отличается от любого другого аргумента функции-члена, так почему вы удивляетесь, что GCC позволяет вам применять restrict к нему?

Что касается применения его кОператор присваивания справедливо указывает на то, что это избавит от необходимости явного теста самопредставления.Затем вы говорите:

Очевидно, что это то, что вы не можете знать заранее

Но это всегда верно при использовании restrict для всего.Например, кто-то может решить вызвать memcpy с перекрывающимися областями памяти;вы «не можете знать заранее», что они этого не сделают.Но объявление restrict для аргументов memcpy означает , что они допустили ошибку, если они .Точно так же, если вы объявляете оператор присваивания restrict, вы допустили ошибку, когда кто-то сам присваивает объекты этого класса.В этом нет ничего таинственного или противоречивого;это всего лишь часть семантики restrict, которая накладывает определенные ограничения на остальную часть вашего кода.

Я также не уверен, почему для функции-члена так сложно получить указатель (илиссылка) на другой объект того же типа.Тривиальный пример:

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

Подобные вещи возникают постоянно.

Итак, настоящий вопрос в том, почему вы думаете, this так отличается от любого другого аргумента?Или, если ты предпочитаешь, как я так упустил твою точку зрения?

2 голосов
/ 22 апреля 2012

Полагаю, вам, ребята, не хватает того, что аргумент функции-члена также может иметь псевдоним parts или объект.Вот пример

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 или нет, поэтому он имеет для принятия наихудшего случая, независимо от того, умный он или нетявляется.Таким образом, есть определенное и очень реальное преимущество для ограничения, даже если оно применяется к указателю this.

1 голос
/ 25 июля 2011

Ссылка, которую вы разместили, интересна. Не будет основательного варианта использования 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;

Хотя это очень необычная практика, это единственный случай, о котором я мог подумать.

0 голосов
/ 25 июля 2011

Добавление этого в качестве ответа, потому что оно, вероятно, лучше подходит как таковое (это своего рода ответ и на самом деле не относится к вопросу, плюс это немного длинно для комментария).

После долгого размышления над ответом Немо, я считаю, что наши оба толкования о самопредставлении, возможно, несколько ошибочны (хотя Немо был более правильным, чем мой). Как правильно отметил Nemo, наличие ограниченного this фактически означает, что наличие псевдонимов является ошибкой программы. Не больше, не меньше.

Поскольку при написании этого ваша логика на самом деле не должна быть "поскольку вы говорите, что это не может произойти, вам, следовательно, не следует проверять самоприсвоение", а скорее ", поскольку вы явно говорите, что псевдоним не может произойти, и это программа ошибка, если это так, вам нужно не только проверять самопредставление, но и последовательно должен терпеть неудачу, если это произойдет ".

И, поскольку он подчеркивает конкретную программную логику и в то же время позволяет компилятору лучше оптимизировать для этого конкретного особого случая, и, таким образом, действительно имеет смысл .

0 голосов
/ 24 июля 2011

Боюсь, что я не совсем понимаю, почему вы говорите, что this.

restrict указывает, что указатель не перекрывается с другими.Таким образом, компиляторы могут предполагать, что области памяти, указанные ограничивающими указателями, не являются зависимыми, что позволяет проводить более агрессивную оптимизацию.__restrict__ будет гораздо более эффективным, когда используется для других переменных-указателей, чем this.

Итак, что может быть в случае, когда кто-то на самом деле захочет дать члену ограничительный квалификатор и где онимеет смысл?

Вспомните типичный случай использования restrict указателей в memcpy:

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