Похоже, что общая концепция заключается в том, что std::unique_ptr не требует лишних затрат времени по сравнению с правильно используемыми владельцами необработанных указателей, достаточная оптимизация.
Но как насчет использования std::unique_ptr в составных структурах данных, в частности std::vector<std::unique_ptr<T>>? Например, изменение размера базовых данных вектора, которое может произойти во время push_back. Чтобы изолировать производительность, я петлю вокруг pop_back, shrink_to_fit, emplace_back:
#include <chrono>
#include <vector>
#include <memory>
#include <iostream>
constexpr size_t size = 1000000;
constexpr size_t repeat = 1000;
using my_clock = std::chrono::high_resolution_clock;
template<class T>
auto test(std::vector<T>& v) {
    v.reserve(size);
    for (size_t i = 0; i < size; i++) {
        v.emplace_back(new int());
    }
    auto t0 = my_clock::now();
    for (int i = 0; i < repeat; i++) {
        auto back = std::move(v.back());
        v.pop_back();
        v.shrink_to_fit();
        if (back == nullptr) throw "don't optimize me away";
        v.emplace_back(std::move(back));
    }
    return my_clock::now() - t0;
}
int main() {
    std::vector<std::unique_ptr<int>> v_u;
    std::vector<int*> v_p;
    auto millis_p = std::chrono::duration_cast<std::chrono::milliseconds>(test(v_p));
    auto millis_u = std::chrono::duration_cast<std::chrono::milliseconds>(test(v_u));
    std::cout << "raw pointer: " << millis_p.count() << " ms, unique_ptr: " << millis_u.count() << " ms\n";
    for (auto p : v_p) delete p; // I don't like memory leaks ;-)
}
Компиляция кода с помощью -O3 -o -march=native -std=c++14 -g с gcc 7.1.0, clang 3.8.0 и 17.0.4 на Linux на Intel Xeon E5-2690 v3 @2.6 ГГц (без турбо):
raw pointer: 2746 ms, unique_ptr: 5140 ms  (gcc)
raw pointer: 2667 ms, unique_ptr: 5529 ms  (clang)
raw pointer: 1448 ms, unique_ptr: 5374 ms  (intel)
Необработанная версия указателя тратит все это время на оптимизированный memmove (у Intel, похоже, намного лучше, чем clang и gcc). Код unique_ptr, по-видимому, сначала скопирует векторные данные из одного блока памяти в другой и назначает исходный нулевой - все в ужасно не оптимизированном цикле. И затем он снова перебирает исходный блок данных, чтобы узнать, отличен ли какой-либо из тех, которые были только нулевыми, и их необходимо удалить. Полную информацию о gory можно увидеть на godbolt.  Вопрос заключается не в том, как скомпилированный код отличается, что довольно ясно. Вопрос:  почему компилятор не может оптимизировать то, что обычно рассматривается как абстракция без лишних затрат.
Попытка понять, как компиляторы рассуждают об обработке std::unique_ptr, я искал немного больше на изолированном коде. Например:
void foo(std::unique_ptr<int>& a, std::unique_ptr<int>& b) {
  a.release();
  a = std::move(b);
}
или аналогичный
a.release();
a.reset(b.release());
ни один из компиляторов x86 похоже, не может оптимизировать бессмысленный if (ptr) delete ptr;. Компилятор Intel даже дает возможность удалить 28%. Удивительно, что проверка удаления последовательно опущена для:
auto tmp = b.release();
a.release();
a.reset(tmp);
Эти биты не являются основным аспектом этого вопроса, но все это заставляет меня чувствовать, что я чего-то не хватает.
Почему различные компиляторы не могут оптимизировать перераспределение в std::vector<std::unique_ptr<int>>? Есть ли что-нибудь в стандарте, которое предотвращает генерацию кода так же эффективно, как с необработанными указателями? Это проблема стандартной реализации библиотеки? Или компиляторы недостаточно недостаточно умны (пока)?
Что можно сделать, чтобы избежать влияния производительности по сравнению с использованием raw-указателей?
Примечание. Предположим, что T является полиморфным и дорогим для перемещения, поэтому std::vector<T> не является вариантом.
