Является ли пустым aliasing shared_ptr хорошей альтернативой no-op удалению shared_ptr?

Иногда мне нужны экземпляры shared_ptr, у которых есть no-op deleter, потому что API ожидает экземпляр shared_ptr, который он хочет хранить в течение ограниченного времени, но мне предоставляется необработанный указатель, которому мне не разрешено владеть на какое-то время больше, чем я бегу.

В этом случае я использую no-op deleter, например [](const void *){}, но сегодня я обнаружил, что есть другая альтернатива этому, используя (или злоупотребляя?) конструктор псевдонимов shared_ptr:

void f(ExpectedClass *ec) {
   std::shared_ptr<ExpectedClass> p(std::shared_ptr<void>(), ec);
   assert(p.use_count() == 0 && p.get() != nullptr);
   apiCall(p);
}

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

Ответ 1

В отношении производительности в следующем эталоне показаны неустойчивые цифры:

#include <chrono>
#include <iostream>
#include <limits>
#include <memory>

template <typename... Args>
auto test(Args&&... args) {
    using clock = std::chrono::high_resolution_clock;
    auto best = clock::duration::max();

    for (int outer = 1; outer < 10000; ++outer) {
        auto now = clock::now();

        for (int inner = 1; inner < 20000; ++inner)
            std::shared_ptr<int> sh(std::forward<Args>(args)...);

        auto time = clock::now()-now;
        if (time < best) {
            best = time;
            outer = 1;
        }
    }

    return best.count();
}

int main()
{
    int j;

    std::cout << "With aliasing ctor: " << test(std::shared_ptr<void>(), &j) << '\n'
              << "With empty deleter: " << test(&j, [] (auto) {});
}

Вывод на моем компьютере с помощью clang++ -march=native -O2:

With aliasing ctor: 11812
With empty deleter: 651502

GCC с идентичными параметрами дает еще больший коэффициент, 5921: 465794.
А Clang с -stdlib=libc++ дает колоссальный 12: 613175.