ограничить квалификатор функций-членов (ограничить этот указатель)

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

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

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

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

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

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


person Damon    schedule 24.07.2011    source источник
comment
Не думаю, что есть смысл так делать. Что именно вы бы удалили с помощью this - на самом деле это не непрерывный массив одного и того же типа?   -  person Anycorn    schedule 24.07.2011
comment
@Anycom: если у класса есть элементы данных, любой совместимый указатель может быть псевдонимом члена данных.   -  person MSalters    schedule 25.07.2011


Ответы (5)


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

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

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

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

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

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

Такое постоянно всплывает.

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

person Nemo    schedule 25.07.2011

Я считаю, что вы, ребята, упускаете из виду, что аргумент функции-члена также может быть псевдонимом 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

что, очевидно, менее эффективно. Обратите внимание, что, если только calculate_something не является встроенным, у компилятора нет способа узнать, может ли результат быть псевдонимом some_value или нет, поэтому он должен предполагать наихудший случай, независимо от того, какой умный или тупой это. Таким образом, существует определенное и очень реальное преимущество для ограничения, даже если оно применяется к указателю this.

person Florian Pflug    schedule 21.04.2012

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

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

person iammilind    schedule 25.07.2011
comment
Хех, забавный пример, хотя я думаю, что это даже не необычно, а скорее то, чего хотелось бы избежать, как из-за того, что это несколько неочевидно, так и из-за странной семантики. Семантика будет чем-то похожа на любой другой указатель на объект, о котором я утверждаю, что это не мой объект, все равно уничтожаю мой объект и изменяю указатель на какой-то другой объект . - person Damon; 25.07.2011
comment
@ Деймон, это верно; но компилятор не может различать очевидное и неочевидное. В любом случае, ограничение указателя this не имеет для меня особого смысла. Посмотрим, есть ли у кого-то лучший ответ. - person iammilind; 25.07.2011

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

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

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

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

void Foo::MyCompute(__restrict__ char* bufA, __restrict__ char* BufB)
{
}
person minjang    schedule 24.07.2011
comment
См. два последних абзаца здесь. Я знаю об использовании restrict для параметров функций или внутри блоков, что, конечно, совершенно ясно. Мой вопрос касается присвоения this ограниченной квалификации (как описано в связанной документации), что мне кажется совершенно бессмысленным. - person Damon; 24.07.2011

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

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

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

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

person Damon    schedule 25.07.2011