Ответ 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 является разделение "алгоритмов" (в этом случае независимо от значений) от "контейнеров" (в этом случае тот факт, что значения хранятся в списке, в отличие от к вектору или массиву или к самому сортировочному набору или просто распечататься без их хранения вообще.)