Ограничить классификатор для функций-членов (ограничить этот указатель)
Примечание: чтобы уточнить, вопрос не об использовании restrict
ключевое слово в целом, но конкретно о применении его к функциям-членам, как описано здесь.
GCC позволяет использовать __restrict__
(GNU++ эквивалент C99 restrict
) классификатор на функцию-член, эффективно делая this
ограниченный квалифицированный указатель в области видимости функции. Где говядина?
Большинство функций-членов работают с другими участниками, получая к ним доступ через this
, который является T* const
(и обычно не сглаженные). За this
чтобы быть псевдонимом, должен быть второй указатель на тип, используемый как-то внутри функции-члена, и он должен был прийти откуда-то.
Это ситуация, которая обычно имеет место с функциями, не являющимися членами, такими как, например, все бинарные операторы или любая другая свободная функция, которая принимает как минимум два указателя или ссылки идентичного нетривиального типа. Однако эти функции не имеют this
так что они не актуальны.
Оператор присваивания, конструктор копирования и унарные операторы сравнения являются примерами функций-членов, где this
в принципе может быть псевдонимом (так как другой объект передается по ссылке). Так что на самом деле имеет смысл присваивать им ограничивающий квалификатор - для компилятора уже должно быть очевидно, что все остальные функции в любом случае имеют свойство restrict (потому что никогда не бывает второго указателя на T).
Теперь, если, например, вы использовали restrict
на operator=
следовательно, вы вообще не должны проверять самоопределение, потому что вы говорите, что this
не является псевдонимом в пределах этой функции (и если это так, самоопределение не может произойти).
Очевидно, это то, что вы не можете знать заранее, и это тоже не имеет смысла.
Итак, что было бы в случае, когда на самом деле хотелось бы дать функции-члену ограничительный квалификатор и где это имеет смысл?
5 ответов
Либо я что-то упустил, либо ваш вопрос не имеет смысла. this
ничем не отличается от любого другого аргумента функции-члена, так почему вы удивляетесь, что GCC позволяет вам применять restrict
к этому?
Что касается применения его к оператору присваивания, вы справедливо указываете, что это устранит необходимость в явном тесте самопредставления. Тогда вы говорите:
Очевидно, что это то, что вы не можете знать заранее
Но это всегда так, когда вы используете restrict
для всего. Например, кто-то может решить позвонить memcpy
с перекрывающимися областями памяти; вы "не можете знать заранее", что они этого не сделают. Но restrict
декларация для аргументов memcpy
означает, что они совершили ошибку, если они делают. Точно так же, если вы объявляете оператор присваивания 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 или нет, поэтому он должен принять наихудший случай, независимо от того, какой он умный или глупый. Таким образом, есть определенное и очень реальное преимущество для ограничения, даже если он применяется к указателю this.
Ссылка, которую вы разместили, интересна. Там не будет серьезного случая использования для 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
,
Итак, что будет в случае, когда кто-то на самом деле захочет дать члену ограничительный квалификатор и где это имеет смысл?
Просто вспомните типичный случай использования restrict
указатели в memcpy
:
void Foo::MyCompute(__restrict__ char* bufA, __restrict__ char* BufB)
{
}
Добавляя это как ответ, потому что он, вероятно, лучше подходит как таковой (это своего рода ответ и на самом деле не относится к вопросу, плюс это немного долго для комментария).
Подумав об ответе Немо в течение длительного времени, я считаю, что обе наши интерпретации о самопредставлении, возможно, несколько ошибочны (хотя Немо был более верным, чем мой). Как правильно отметил Немо, имея ограниченную квалификацию this
фактически означает, что наличие псевдонимов является ошибкой программы. Не больше, не меньше.
Поскольку, когда вы пишете это, ваша логика на самом деле не должна быть "поскольку вы говорите, что это не может произойти, вы, следовательно, не должны проверять самоназначение", а скорее ", поскольку вы явно говорите, что псевдоним не может произойти, и это программная ошибка, если она да, вам нужно не только проверять самопредставление, но и, следовательно, вы должны терпеть неудачу, если это произойдет ".
И, поскольку он подчеркивает конкретную логику программы и в то же время позволяет компилятору лучше оптимизировать для этого конкретного частного случая, и, таким образом, он действительно имеет смысл.