C++26 приносит новый инструмент для старого доброго идиома PImpl — «Pointer to implementation». Суть метода проста: спрятать детали реализации класса за непрозрачным указателем, чтобы развести интерфейс и реализацию по разным заголовочным файлам и ускорить компиляцию.
При классическом подходе с сырым указателем в классе Widget объявляется вложенная структура Impl, а в .cpp-файле она определяется и хранит состояние — например, счётчик кликов и имя кнопки. Код обрастает «правилом пяти»: деструктор, копирующие и перемещающие конструктор с оператором присваивания приходится писать вручную, аккуратно управляя памятью через new и delete. При этом остаётся две неприятные проблемы. Константность не пробрасывается: в методе, помеченном const, всё равно можно изменить данные, потому что константным оказывается только указатель, а не сам объект Impl. После перемещения указатель у исходного объекта обнуляется, и любой вызов метода ведёт к неопределённому поведению.
std::unique_ptr упрощает управление памятью — можно задефолтить деструктор и перемещающие операции, но копирующие всё равно требуют ручной реализации. Проблема с константностью и висячим нулевым указателем после перемещения никуда не девается.
Новый std::indirect из заголовка <memory> решает обе эти проблемы. Он владеет объектом в куче, но ведёт себя как обычное значение. Копирование std::indirect делает глубокую копию хранимого объекта, поэтому компилятор сам сгенерирует корректный копирующий конструктор. Константность пробрасывается правильно: через const std::indirect<Impl> вы получаете только константный доступ к Impl. Тип никогда не бывает пустым, кроме состояния «после перемещения», но для проверки этого есть метод valueless_after_move(). С его помощью можно вставить assert в каждую функцию-член или аккуратно удалить «опустошённые» элементы из вектора через std::erase_if.
Специальные функции-члены по-прежнему нужно объявить в заголовочном файле и задефолтить в .cpp, где Impl становится полным типом. На момент написания статьи только GCC 16 поддерживает std::indirect.