2013-10-02 10 views
7

Wdrażam klasę fabryki, która buduje wektor z uint8_t. Chcę móc wykorzystać semantykę ruchu podczas zwracania wynikowego wektora. To wydaje się działać, ale nie jestem pewien, czy to jest właściwy sposób na osiągnięcie tego, co chcę.Jak używać semantyki ruchu podczas zwracania zmiennej składowej?

Widziałem kilka przykładów tego, jak zwracana zmienna automatyczna będzie uważana za wartość rwartą i użyje konstruktora ruchu kodu wywołującego, ale w moim przykładzie zwróconym obiektem jest element członkowski. Wiem, że członek straci swoją zawartość, jeśli wywołujący umieści wartość zwracaną w konstruktorze ruchu - i właśnie tego chcę.

Pisałem to mniej więcej tak:

#include <cstdint> 
#include <iostream> 
#include <vector> 

class Factory 
{ 
public: 
    std::vector<uint8_t> _data; 

    Factory(std::size_t size) : 
     _data(size, 0) 
    { 
    } 

    void buildContent(int param) 
    { 
     // perform operations on the contents of _data 
    } 

    std::vector<uint8_t> && data() 
    { 
     return std::move(_data); 
    } 
}; 

int main() 
{ 
    Factory factory(42); 
    factory.buildContent(1); 
    std::vector<uint8_t> temp(factory.data()); 
    std::cout << "temp has " << temp.size() << " elements" << std::endl; 
    std::cout << "factory._data has " << factory._data.size() << " elements" << std::endl; 

    return 0; 
} 

Edit:

Aha, i przykładowy kod generuje następujące:

temp has 42 elements 
factory._data has 0 elements 
+1

Więc jakie jest twoje pytanie? – DanielKO

+0

"Nie jestem pewien, czy to jest właściwy sposób na osiągnięcie tego, czego chcę." – Geoffroy

+0

Och, teraz widzę. Sprawdź to: http://stackoverflow.com/questions/9914548/using-of-rvalue-references-in-c11 – DanielKO

Odpowiedz

5

Przede wszystkim, trzeba decydować, co chcesz. Czy na pewno chcesz wyssać dane z instancji? Jeśli tak, to co udało ci się osiągnąć. Z drugiej strony wygląda całkiem niebezpiecznie. Co można zrobić, to użyć kwalifikacyjne referencyjne do wyegzekwowania, że ​​tylko powrót odwołanie rvalue gdy sama instancja jest odniesienie RValue:

std::vector<uint8_t> && data() && // called on rvalue 
{ 
    return std::move(_data); 
} 

// return lvalue ref or value 
std::vector<uint8_t>& data() & // called on lvalue 
{ 
    return _data; 
} 

W końcu, to wszystko zależy od problemu, który próbujesz rozwiązać, który nie wynika z twojego pytania.

+0

czy istnieje kompilator obsługujący taką składnię? (&& po deklaracji metody) –

+0

@ BЈовић [Wygląda na to, GCC 4.8.1] (http://gcc.gnu.org/projects/cxx0x.html), chociaż tego nie przetestowałem. – juanchopanza

1

Jeśli Twój kompilator ma odniesienia rAW do tego (&& po metodach), możesz użyć tego. Zobacz odpowiedź @ juanchopanza.

Jeśli nie, najpierw upewnij się, że data() wyjaśnia, że ​​się poruszasz. Jest na to kilka sposobów.

Po pierwsze, metody nie będące członkami (friend s) można przesłonić na &&. Więc można dostać tę składnię:

std::vector<uint8_t> temp(get_data(std::move(factory)); 

gdzie get_data ma && i & przeciążeń od typu factory i albo porusza się lub nie opiera się to.

Następnie należy zwrócić std::vector<uint8_t> zamiast `std::vector<uint8_t>&& w przypadku problemów z wydłużonym czasem użytkowania. Koszt runtime jest gdzieś od zera do małego, ale wyeliminowane błędy są warte zachodu.

Jeśli create_factory zwraca Factory obiekt, a następnie, jeśli to zrobimy:

for(uint8_t x : get_data(create_factory())) 

get_data która zwraca && nie działa, a ten, który zwraca tymczasowe działa bez zarzutu.

Co się dzieje? Cóż, pętle oparte na odległościach są zdefiniowane jako wiążące rzecz, do której się odwołujesz. Ograniczenie czasowe do odniesienia ma przedłużony czas życia: odniesienie do odniesienia nie ma przedłużenia dożywotniego. Czas życia wartości zwrotnej funkcji create_factory nie jest w żadnym przypadku wydłużany.

W przypadku && odniesienie do wektora pozostaje zwisające. W przypadku tymczasowego zwrotu, wektor factory zostaje przeniesiony do tymczasowego, a następnie zostaje wydłużony czas istnienia tymczasowych.

Krótko mówiąc, powracające referencje && bardzo rzadko są dobrym pomysłem.

0

Co powiesz na ukrywanie operatora przydziału w swoim wektorze i tylko zaimplementowanie operatora przypisania ruchu (zasadniczo uniemożliwiającego kopiowanie - jeśli tego chcesz).

Więc tak:

std::vector<uint8_t> temp(factory.data()); 

nie będzie kompilować, chyba że zmieni to do tego:

std::vector<uint8_t> temp(std::move(factory)); 
+0

Co zrobić, aby ukryć konstruktora kopii lub operatora przypisania std :: vector? – anorm

+0

Chciałem powiedzieć, że jeśli zmienisz znaczenie swojej fabryki, aby działać bardziej jak przedłużenie wektora przez dziedziczenie (wektor IS-A) lub zawijanie go (wektor HAS-A), twoi klienci będą wtedy zajmować się fabryką (które musiałbyś zmienić, aby odzwierciedlić nowe znaczenie). Alternatywnie możesz stworzyć wektor, który nie może być kopiowany i zwrócić go z fabryki zamiast std :: vector, ale to może wydawać się zbyt skomplikowane. Problem z twoim podejściem polega na tym, że wywołanie funkcji facotry.data() unieważni te dane, które mogą być mylące. – Sereger

Powiązane problemy