2017-01-31 13 views
5

Korzystanie z obiektu std::shared_ptr wyraża współwłasność i opcjonalność (z możliwością uzyskania zerowej wartości).shared_ptr, które nie może być puste?

Znajduję się w sytuacjach, w których chcę wyrazić współwłasność tylko w moim kodzie, bez żadnej opcjonalności. Używając parametru shared_ptr jako parametru funkcji, muszę pozwolić, aby funkcja sprawdziła, czy nie jest równa zero, aby była spójna/bezpieczna.

Podanie odniesienia zamiast oczywiście jest opcją w wielu przypadkach, ale czasami chciałbym również przenieść prawo własności, jak to jest możliwe z shared_ptr.

Czy istnieje klasa, która może zastąpić shared_ptr bez możliwości unieważnienia, jakąś konwencję, aby poradzić sobie z tym problemem, czy moje pytanie nie ma większego sensu?

+8

Należy zamiast tego podać referencję (const). –

+0

http://stackoverflow.com/questions/11365149/missing-shared-ref –

+1

^Nie ma zbyt wielu przypadków użycia, aby przekazać wartość 'shared_ptr' według wartości. –

Odpowiedz

2

można napisać otoki wokół std::shared_ptr który umożliwia utworzenie tylko z niezerowe:

#include <memory> 
#include <cassert> 

template <typename T> 
class shared_reference 
{ 
    std::shared_ptr<T> m_ptr; 
    shared_reference(T* value) :m_ptr(value) { assert(value != nullptr); } 

public: 
    shared_reference(const shared_reference&) = default; 
    shared_reference(shared_reference&&) = default; 
    ~shared_reference() = default; 

    T* operator->() { return m_ptr.get(); } 
    const T* operator->() const { return m_ptr.get(); } 

    T& operator*() { return *m_ptr.get(); } 
    const T& operator*() const { return *m_ptr.get(); } 

    template <typename XT, typename...XTypes> 
    friend shared_reference<XT> make_shared_reference(XTypes&&...args); 

}; 


template <typename T, typename...Types> 
shared_reference<T> make_shared_reference(Types&&...args) 
{ 
    return shared_reference<T>(new T(std::forward<Types>(args)...)); 
} 

Zwróć uwagę, że operator= brakuje jeszcze. Powinieneś zdecydowanie to dodać.

Można go używać tak:

#include <iostream> 


using std::cout; 
using std::endl; 

struct test 
{ 
    int m_x; 

    test(int x)   :m_x(x)     { cout << "test("<<m_x<<")" << endl; } 
    test(const test& t) :m_x(t.m_x)    { cout << "test(const test& " << m_x << ")" << endl; } 
    test(test&& t)  :m_x(std::move(t.m_x)) { cout << "test(test&& " << m_x << ")" << endl; } 

    test& operator=(int x)   { m_x = x;     cout << "test::operator=(" << m_x << ")" << endl; return *this;} 
    test& operator=(const test& t) { m_x = t.m_x;    cout << "test::operator=(const test& " << m_x << ")" << endl; return *this;} 
    test& operator=(test&& t)  { m_x = std::move(t.m_x); cout << "test::operator=(test&& " << m_x << ")" << endl; return *this;} 

    ~test()    { cout << "~test(" << m_x << ")" << endl; } 
}; 

#include <string> 

int main() { 

    { 
     auto ref = make_shared_reference<test>(1); 
     auto ref2 = ref; 

     *ref2 = test(5); 
    } 
    { 
     test o(2); 
     auto ref = make_shared_reference<test>(std::move(o)); 
    } 

    //Invalid case 
    //{ 
    // test& a = *(test*)nullptr; 
    // auto ref = make_shared_reference<test>(a); 
    //} 
} 

wyjściowa:

test(1) 
test(5) 
test::operator=(test&& 5) 
~test(5) 
~test(5) 
test(2) 
test(test&& 2) 
~test(2) 
~test(2) 

Example on Coliru

Mam nadzieję, że nie wszystko, co może doprowadzić do nieokreślonego zachowania zapomnieć.

+0

Dzięki. Dokładnie to, co miałem na myśli. Ale dlaczego potrzebuje niestandardowego 'operatora ='? Domyślna wydaje się działać dobrze. –

+0

Ze względu na ['rule-of-five'] (http://en.cppreference.com/w/cpp/language/rule_of_three). –

+0

OK, rozumiem regułę. Dlaczego w tym przypadku potrzebne jest zadeklarowanie funkcji i ich domyślne? Myślę, że masz na myśli [like that] (http://coliru.stacked-crooked.com/a/500ba6ae0d885a31). Czy nie możemy po prostu ich całkowicie pominąć [http://ec.europa.eu/documentation/detail.asp?url=http://www.coliru.stacked-crooked.com/a/0a003a4bbd369119). Czy to ma negatywne skutki, których jeszcze nie widzę? –

3

Pytasz o klasę opakowania klasy not_null. Na szczęście Twój problem został już rozwiązany przez ekspertów C++ guideline i istnieją już przykłady implementacji - takich jak ten one. Wyszukaj szablon klasy not_null.

Powiązane problemy