Przypisać nieprzypisywalne
Algorytmy takie jak std::copy
czy std::transform
działają w ten sposób, że
przyjmują pewien OutputIterator
i podmieniają wartość przezeń wskazywaną nową
wartością:
template <typename OutputIt, typename InputIt>
OutputIt copy(InputIt from, InputIt to, OutputIt out)
{
while (from != to)
{
*out = *from;
++out;
++from;
}
return out;
}
Co jednak zrobić w sytuacji, gdy nasz typ nie posiada operatora przypisania?
Kiedy to się zdarzy?
Sytuacja taka może się zdarzyć na przykład wtedy gdy jest on domyślnie skasowany
(implicitly deleted), czyli np. gdy nasza klasa bądź struktura będzie miała
const
membera:
struct Foo
{
Foo() : i(0) {}
Foo(int i) : i(i) {}
const int i;
};
std::vector<Foo> v1{ {1}, {2}, {3} };
std::vector<Foo> v2(v1.size());
// To nie zadziała:
std::copy(v1.begin(), v1.end(), v2.begin());
Foo
nie posiada operatora przypisania z oczywistych względów: mógłby on
modyfikować stałą i
. Jednak posiada konstruktor kopiujący. Jak zatem zmusić
std::copy
do faktycznego skopiowania obiektu, zamiast przypisania (copy
assignment)?
Jak żyć?
Na ratunek przychodzą iteratory pomocnicze typu std::insert_iterator
,
std::back_insert_operator
itp. Ich implementacja dostarcza operator
przypisania value_type
z danego kontenera do samego iteratora, wewnątrz
którego kryje się faktyczna implementacja. Coś w tym stylu:
template <typename Container>
class back_insert_iterator
{
// ...
back_insert_iterator& operator=(const typename Container::value_type& val)
{
c.push_back(val);
return *this;
}
back_insert_iterator& operator*() { return *this; }
back_insert_iterator operator++() { return *this; }
back_insert_iterator operator++(int) { return *this; }
};
Dzięki temu, oraz dzięki temu, że operatory wyłuskania i inkrementacji zwracają
*this
, nie zostanie wywołany Foo::operator=(const Foo&)
, ale
back_insert_iterator::operator=(const Foo&)
. Oznacza to ni mniej, ni więcej,
że cel skopiowania danych z jednego kontenera do drugiego został osiągnięty:
std::vector<Foo> v1{ {1}, {2}, {3} };
std::vector<Foo> v2;
// To zadziała:
std::copy(v1.begin(), v1.end(), std::back_inserter(v2));