Подтвердить что ты не робот

Правила псевдонимов С++

Просто интересно, подтвердит ли кто-нибудь несколько правил псевдонимов для меня.

Я знаю, что сглаживание (например, проблемы с загрузкой) может привести к тому, что следующий тип кода будет субоптимальным, потому что мы не можем предположить, что x, y, z не перекрываются:

// case 1:
void plus(size_t n, double *x, double *y, double *z)
{
    for (size_t i = 0; i != n; ++i)
        z[i] = x[i] + y[i];
} 

Я знаю, что есть ключевое слово C __restrict, которое подсказывает компилятору, что он не должен рассматривать перекрывающийся случай и, следовательно, потенциально генерировать лучший код:

// case 2:
void plus(size_t n, double *__restrict x, double *__restrict y, double *__restrict z)
{ // as above... }

Но как сглаживание работает с кодом стиля С++, где мы будем иметь дело с контейнерными объектами, переданными по ссылке, а не с примерами, подобными C, с исходными указателями?

Например, я предполагаю, что будут проблемы с псевдонимом, если мы сделаем следующее:

// case 3:
void plus(std::vector<double> &x, std::vector<double> &y, std::vector<double> &z)
{ // similar to above... }

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

// case 4:
void foo(std::vector<mytype1> &x, std::vector<mytype2> &y)
{ // interwoven operations on x, y... }

Я не пытаюсь оптимизировать микро-оптимизацию, но мне интересно, лучше ли в настоящее время передавать ограниченные указатели на контейнеры, а не ссылки.

EDIT: Чтобы прояснить некоторые термины, как указано: restrict - это ключевое слово C99. Там также __restrict и __restrict__ в разных компиляторах, но все они делают то же самое.

4b9b3361

Ответ 1

В соответствии с правилом строгого сглаживания вам не разрешается псевдоним одной и той же памяти с указателями на разные типы (кроме char* и друзей), поэтому case 4 может применяться только в том случае, если один из типов был char*.

Случай 3, хотя и не все, что отличается от случая 1, поскольку ссылки реализованы как указатели на все компиляторы, которые я знаю, хотя стандарт не требует этого, и реализация может придумать что-то еще.

Ответ 2

Это не совсем специфично для С++. Рассмотрим этот бит C99:

struct vector {
    double* data;
    size_t n;
};

void
plus(struct vector* restrict x, struct vector* restrict y, struct vector* restrict z)
{
    // same deal as ever
}

Здесь restrict покупает нас очень мало: x->data, y->data и z->data - все double* и разрешены к псевдониму. Это точно так же, как в случае 1, даже при использовании ограничения.

Если в С++ было ключевое слово restrict (или при использовании расширения), наилучшим вариантом, вероятно, было бы сделать plus(vecA.size(), &vecA[0], &vecB[0], &vecB[0]), используя тот же plus, что и в случае 2. И на самом деле это можно сделать это прямо сейчас, используя интерфейс в стиле C89 без restrict, но который использует ключевое слово под обложками.