Перемещение shared_ptr <StructA>в shared_ptr > - PullRequest
5 голосов
/ 13 июля 2020

Я пытаюсь избежать довольно сложной цепочки наследования для набора простых старых структур данных, но мне нужно хранить их все в векторе + долевое владение с вызывающим.

Struct A {};
Struct B {};

using TInstruction = std::variant<A, B>;

struct holder {
  std::vector<std::shared_ptr<TInstruction>> items;
};

static holder h;

// Every individual TInstruction gets its own add function, for validation purposes
void add(std::shared_ptr<A> a) {
  // Somehow add this to h, while still sharing ownership with the caller
  // h.emplace_back(???)
}


int main() {
  holder h;

  auto a = std::make_unique<A>();
  auto b = std::make_unique<B>();

  add(a);
  // add(b) // not implemented
}

I У меня был умеренный (но досадно слабый) успех со следующими изменениями в исходной идее:

  1. Использовать общие указатели внутри варианта, т.е. using TInstruction = std::variant<std::shared_ptr<A>, std::shared_ptr<B>>
  2. Принятие std :: weak_ptr в add() и используйте .lock(), чтобы превратить его в std :: shared_ptr

Я не против # 2 (мне кажется, что это может быть правильный способ сделать это ), но сохранение shared_ptr внутри варианта внутри «снаружи» приводит к очень подробному коду и сопоставлению с образцом.

Возможно ли это вообще? По сути, я хочу изменить тип общего указателя, но все же express идею совместного владения.

Ответы [ 2 ]

1 голос
/ 14 июля 2020

Вместо использования variant вы можете воспользоваться shared_ptr<void> возможностью удерживать shared_ptr за что угодно, если вы сами отслеживаете тип, который он держит, например:

// Generic typelist
template <typename...>
struct Types;

// Find the 0-based index of type T in Types<...>
template <typename, typename>
struct Index;

// T is the first type in Types<...>
template <typename T, typename... Us> 
struct Index<T, Types<T, Us...>> : std::integral_constant<int, 0> {}; 

// T is not the first type in Types<...>
template <typename T, typename U, typename... Us> 
struct Index<T, Types<U, Us...>>
    : std::integral_constant<int, 1 + Index<T, Types<Us...>>()> {}; 

template <typename... Ts> 
struct SharedPtrVariant {
    template <typename T>
    explicit SharedPtrVariant(std::shared_ptr<T> p)
        : sp(std::move(p)), index(Index<T, Types<Ts...>>()) {}

    template <typename T>
    std::shared_ptr<T> get() const {
        return std::static_pointer_cast<T>(
            Index<T, Types<Ts...>>() == index ? sp : nullptr);
    }   

   private:
    std::shared_ptr<void> sp; 
    int index;
};
0 голосов
/ 14 июля 2020

Согласно комментариям, это несколько глупый вопрос, потому что вариант должен быть владельцем объекта, чтобы окончательный тип был shared_pointer<variant>. Предложение Невина сработает, но использует тип void, у которого есть свои проблемы (возможно, можно было бы использовать любой тип? Это было бы не лучше.)

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

...