• Najnowsze pytania
  • Bez odpowiedzi
  • Zadaj pytanie
  • Kategorie
  • Tagi
  • Zdobyte punkty
  • Ekipa ninja
  • IRC
  • FAQ
  • Regulamin
  • Książki warte uwagi

Inicjalizacja standardowego kontenera z unique_ptr [C++]

Object Storage Arubacloud
+1 głos
650 wizyt
pytanie zadane 23 lutego 2017 w C i C++ przez draghan VIP (106,230 p.)

Witam.

Załóżmy, że mam abstrakcyjną klasę Interface, jej dziedzica Child i klasę Keeper, która ma za zadanie zarządzać jakimś zbiorem obiektów, dziedziczących po Interface (w przykładzie mam tylko Child, ale powiedzmy, że dzieci jest więcej).

W jaki sposób mogę zainicjalizować instancję Keeper, tak aby w konstruktorze podawać elementy zbioru (patrz przykład w main())?

Nasuwające się std::initializer_list odpada, z racji kopiowania elementów, którego std::unique_ptr zabrania.

class Interface;

class Child: public Interface
{
public:
    Child(int i);
    // ...
};

class Keeper
{
public:
    Keeper(); // ???

    // ...
private:
    std::vector< std::unique_ptr<Interface> > storage;
};

int main()
{
    Keeper keeper{ std::make_unique<Child>(1),
                   std::make_unique<Child>(2),
                   std::make_unique<Child>(3) };
}

 

2 odpowiedzi

+5 głosów
odpowiedź 23 lutego 2017 przez kq Użytkownik (540 p.)
wybrane 23 lutego 2017 przez draghan
 
Najlepsza

Z std::initializer_list się nie da, bo jej begin() i end() zwracają const E*, więc i tak zostanie wybrany usunięty copy constructor.

Wymodziłem za to coś takiego:

template<typename Receiver, typename Tup, size_t... Is>
void move_assign_seq(Receiver& r, Tup&& tup, std::index_sequence<Is...>) {
	int ignored[] = {
		((void)(r[Is] = std::move(std::get<Is>(tup))), 0)...
	};
	(void)ignored;
}

class Keeper
{
public:
	Keeper(); // ???

	template<typename... Ts>
	Keeper(std::unique_ptr<Ts>&&... items)
	{
		storage.resize(sizeof...(items));
		move_assign_seq(storage, std::forward_as_tuple(std::move(items)...), std::make_index_sequence<sizeof...(items)>());
	}

private:
	std::vector< std::unique_ptr<Interface> > storage;
};

http://melpon.org/wandbox/permlink/0Wus2DbNe6CFhWN1

komentarz 23 lutego 2017 przez criss Mędrzec (172,590 p.)
He, nie wiedziałem, że topka polskiego cpp się tu zjawia :D

To jeszcze nie mój poziom i nie wszystko rozumiem, ale... działa. Nice.
komentarz 23 lutego 2017 przez kq Użytkownik (540 p.)
Topka jak topka ;​) Ale widziałem linka na ircu i zostałem zainteresowany.
komentarz 23 lutego 2017 przez draghan VIP (106,230 p.)
edycja 23 lutego 2017 przez draghan

Super, dzięki za zainteresowanie. Zgrabności kodu nie ocenię, bo nie czuję się kompetentny. Ale działa, więc nic innego mi nie pozostaje, jak oznaczyć odpowiedź jako najlepszą.

Nie rozumiem w Twoim kodzie użycia ... w tych miejscach:

std::move(std::get<Is>(tup))), 0)...    // (1)
storage.resize(sizeof...(items));       // (2)

nawet nie za bardzo wiem, pod jakim hasłem tego szukać. Chyba że to jest coś związanego z vararg, ale... i tak nie łapię. Podpowiesz? :)

Odrobinę zastanawia mnie jedno. Czy mój przykład jest aż tak wybujały i niedorzeczny, że trzeba tu stosować tak egzotyczne rozwiązanie?

EDIT: ... (2) to "po prostu" pobranie rozmiaru wszystkich itemów?

EDIT2: już rozumiem (dzięki Criss ;).

1
komentarz 23 lutego 2017 przez criss Mędrzec (172,590 p.)
edycja 23 lutego 2017 przez criss

sizeof... to osobny operator przeznaczony specjalnie do określania ilości argumentów w przypadku "parameter pack". I tyle wiem. Co do (1) się nie wypowiem.

Btw... Nigdzie nie moge znaleźć dokumentacji std::index_sequence... Dość dziwne.

2
komentarz 23 lutego 2017 przez draghan VIP (106,230 p.)

Btw... Nigdzie nie moge znaleźć dokumentacji std::index_sequence... Dość dziwne.

Paragraf "Helper templates".

komentarz 23 lutego 2017 przez criss Mędrzec (172,590 p.)
O, dzięki.

Dorzuciłem link do dokumentacji nt. sizeof..., bo ciężko to znaleźć w googlach.
0 głosów
odpowiedź 23 lutego 2017 przez mokrowski Mędrzec (155,460 p.)

Można pokazać przejęcie własności przez podanie r-referencji.

#include <vector>
#include <memory>
#include <initializer_list>

class Interface {
};
 
class Child: public Interface
{
public:
    Child(int i) {}
    // ...
};
 
class Keeper
{
public:
    Keeper(std::initializer_list<std::unique_ptr<Child>>&& il) {}
 
    // ...
private:
    std::vector<std::unique_ptr<Interface>> storage;
};
 
int main()
{
    std::initializer_list<std::unique_ptr<Child>> il{std::make_unique<Child>(1),
                   std::make_unique<Child>(2),
                   std::make_unique<Child>(3) };
    Keeper k1{std::move(il)};

    Keeper k2{std::make_unique<Child>(40), std::make_unique<Child>(33),
        std::make_unique<Child>(34)};
}

 

komentarz 23 lutego 2017 przez adrian17 Ekspert (344,860 p.)

To nie jest w pełni działający przykład - inicjalizacja storage w prosty sposób nie działa:

Keeper(std::initializer_list<std::unique_ptr<Child>>&& il): storage(il) {} // error

 

komentarz 23 lutego 2017 przez draghan VIP (106,230 p.)
mokrowski - dziękuję za odpowiedź, jednak nie tutaj tkwi sedno problemu (być może opis jest zbyt ubogi).

Jak pisze adrian17, inicjalizacja prosta "nie działa". Wypełnianie storage za pomocą metod rozszerzających kontener też średnio mi idzie (polecam spróbować, świetna zabawa :).

Jeśli istnieje rozwiązanie, to sądzę że trzeba tutaj zastosować jakąś
"sztuczkę-kombinację" z std::iterator_move, jednak nie potrafię tego złożyć w działającą całość.
(chyba żeby jeszcze kombinować z funkcją o zmiennej liczbie argumentów, ale - jeśli się da - wolałbym tego uniknąć)

Podobne pytania

0 głosów
2 odpowiedzi 299 wizyt
pytanie zadane 12 sierpnia 2021 w C i C++ przez Pawel1995 Gaduła (3,810 p.)
0 głosów
1 odpowiedź 154 wizyt
0 głosów
2 odpowiedzi 237 wizyt

92,539 zapytań

141,382 odpowiedzi

319,477 komentarzy

61,928 pasjonatów

Motyw:

Akcja Pajacyk

Pajacyk od wielu lat dożywia dzieci. Pomóż klikając w zielony brzuszek na stronie. Dziękujemy! ♡

Oto polecana książka warta uwagi.
Pełną listę książek znajdziesz tutaj.

Akademia Sekuraka

Kolejna edycja największej imprezy hakerskiej w Polsce, czyli Mega Sekurak Hacking Party odbędzie się już 20 maja 2024r. Z tej okazji mamy dla Was kod: pasjamshp - jeżeli wpiszecie go w koszyku, to wówczas otrzymacie 40% zniżki na bilet w wersji standard!

Więcej informacji na temat imprezy znajdziecie tutaj. Dziękujemy ekipie Sekuraka za taką fajną zniżkę dla wszystkich Pasjonatów!

Akademia Sekuraka

Niedawno wystartował dodruk tej świetnej, rozchwytywanej książki (około 940 stron). Mamy dla Was kod: pasja (wpiszcie go w koszyku), dzięki któremu otrzymujemy 10% zniżki - dziękujemy zaprzyjaźnionej ekipie Sekuraka za taki bonus dla Pasjonatów! Książka to pierwszy tom z serii o ITsec, który łagodnie wprowadzi w świat bezpieczeństwa IT każdą osobę - warto, polecamy!

...