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

Возвращает std:: list дорого?

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

4b9b3361

Ответ 1

Если вы вернете значение std::list по значению, оно не будет просто скопировать заголовок списка, он скопирует один список node за элемент в списке. Так что да, для большого списка это дорого.

Если список встроен в функцию, возвращающую его, тогда вы можете воспользоваться оптимизацией значений именованного значения, чтобы избежать ненужной копии. Это специфично для вашего компилятора. Это никогда не применяется, если, например, список уже существовал до вызова функции (например, если это переменная-член объекта).

Общая идиома в С++, чтобы избежать возврата контейнеров по значению, заключается в том, чтобы принимать итератор вывода в качестве параметра. Поэтому вместо:

std::list<int> getListOfInts() {
    std::list<int> l;
    for (int i = 0; i < 10; ++i) {
        l.push_back(i);
    }
    return l;
}

Вы делаете:

template<typename OutputIterator>
void getInts(OutputIterator out) {
    for (int i = 0; i < 10; ++i) {
        *(out++) = i;
    }
}

Затем вызывающий выполняет:

std::list<int> l;
getInts(std::back_inserter(l));

Часто, когда компилятор завершил встраивание и оптимизацию, код более или менее идентичен.

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

Недостатки такие же, как и с любым кодом шаблона: реализация должна быть доступна вызывающему абоненту во время компиляции, и вы можете получить много "повторяющегося" объектного кода для нескольких экземпляров шаблона. Конечно, вы можете использовать один и тот же шаблон без использования шаблонов, указав указатель на функцию (плюс указатель пользовательских данных) в качестве параметра и вызывая его один раз с каждым элементом или определяя абстрактный класс IntVisitor с чистым виртуальным членом функция, и наличие вызывающего абонента предоставляет ему экземпляр.

[Edit: T.E.D указывает в комментарии, что другой способ избежать копирования без использования шаблонов - это передать вызывающему абоненту в список по ссылке. Это, безусловно, работает, это просто дает абоненту меньше гибкости, чем шаблон, и, следовательно, не является идиомой, используемой STL. Это хороший вариант, если вы не хотите "преимущества этого", описанного выше. Однако одним из первоначальных намерений STL является разделение "алгоритмов" (в этом случае независимо от значений) от "контейнеров" (в этом случае тот факт, что значения хранятся в списке, в отличие от к вектору или массиву или к самому сортировочному набору или просто распечататься без их хранения вообще.)

Ответ 2

Это (как всегда) зависит. Конструктор копирования может быть вызван или не вызван возвратом в следующем коде.

std::list<int> foo() {
  std::list<int> bar;
  // ...
  return bar;
};

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

Лично я не беспокоюсь об этом и вернусь прямо. Затем, только когда мой профайлер говорит, что это проблема, я рассматриваю оптимизацию.

Ответ 3

Если вы вернетесь по значению, будет вызываться конструктор копирования, и элементы будут копироваться один за другим. Иногда вы будете сохраняться с помощью оптимизации названного значения, как указано на одном месте.

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

  • Перейдите в список по ссылке для заполнения функцией. Таким образом, вы указываете функцию, в которой нужно поместить данные, и никакая копия не потребуется, потому что вы поместите ее в нее в конечном месте.
  • Выделите список в куче и верните его. Вы должны вернуть его в интеллектуальном указателе, таком как std:: auto_ptr или boost:: shared_ptr, чтобы гарантировать, что он будет удален и будет безопасным для исключений.

Ответ 4

Я считаю, вызывается экземпляр-копия.

Ответ 5

Это может быть дорогостоящим, поскольку он скопирует каждый элемент в списке. Что еще более важно, у него другое поведение: вам нужна копия списка или вы хотите, чтобы указатель на исходный список?