chain of function calls and temporary variables with destructors

I am trying to create something like unique pointer for Arduino.
Here is a simple example:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <iostream>

template<typename T>
class Pointer {
    T *pointer;
public:
    Pointer(T *pointer) : pointer(pointer) {}

    virtual ~Pointer() {
        delete pointer;
        pointer = nullptr;
        std::cout << "Pointer is deleted \n";
    }

    T *get() const {
        return pointer;
    }
};

Pointer<int> getPointer() {
    return {new int{5}};
}

int main() {
    auto value = *getPointer().get();
    std::cout << value;

    return 0;
}


as I understand in this line "*getPointer().get();" pointer object is immediately deleted as I don't assign it to local variable, that invokes Pointer's destructor, therefore corrupts original data.

Does it mean that I cannot use chain of function calls in the statement?

Last edited on
as I understand in this line "*getPointer().get();" pointer object is immediately deleted as I don't assign it to local variable, that invokes Pointer's destructor, therefore corrupts original data.

You're forgetting that value is a copy of the referenced object. The original object is destroyed, but the copy lives on.

If instead you cached a raw pointer (i.e., you didn't transfer ownership through Pointer):
int *value = getPointer().get(); // note: no dereferencing operator
Then *value's lifetime would end at the semicolon, but this isn't what happens in the code you posted.
Last edited on
Maybe try something like the following: The class skeleton is pinched from the real one, with the complicated stuff removed.
Caveat: untested.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#include <utility>

// NOTE: std::move() is important for correct use of unique_ptr.
// If std::move isn't provided by <utility>, or <utility> doesn't exist,
// uncomment and use the following move() function:

// template <typename T> struct remove_reference      { using type = T; };
// template <typename T> struct remove_reference<T&>  { using type = T; };
// template <typename T> struct remove_reference<T&&> { using type = T; };
// template <typename T>
// constexpr typename remove_reference<T>::type&& move(T&& x) noexcept
// { return static_cast<typename remove_reference<T>::type&&>(x); }

template <typename T> class unique_ptr {
public:
  using pointer = T *;
  using element_type = T;

  // [unique.ptr.single.ctor], constructors
  constexpr unique_ptr() noexcept = default;
  constexpr unique_ptr(std::nullptr_t) noexcept {}

  unique_ptr(pointer p) noexcept : data(p) {};
  unique_ptr(unique_ptr &&u) noexcept : data(release(u)) {}

  ~unique_ptr() { delete data; }

  // [unique.ptr.single.asgn], assignment
  unique_ptr &operator=(unique_ptr &&u) noexcept {
    reset(std::move(u).release());
    return *this;
  }
  unique_ptr &operator=(std::nullptr_t) noexcept {
    reset();
    return *this;
  }

  // [unique.ptr.single.observers], observers
  T &operator*() const { return *data; };
  pointer operator->() const noexcept { return data; };
  pointer get() const noexcept { return data; };
  explicit operator bool() const noexcept { return data != nullptr; };

  // [unique.ptr.single.modifiers], modifiers
  pointer release() noexcept {
    pointer result = data;
    data = nullptr;
    return result;
  }

  void reset(pointer p = pointer()) noexcept {
    delete data;
    data = p;
  }

  void swap(unique_ptr &u) noexcept {
    using std::swap;
    swap(u.data, this->data);
  }

  // disable copy from lvalue
  unique_ptr(const unique_ptr &) = delete;
  unique_ptr &operator=(const unique_ptr &) = delete;

private:
  pointer data = nullptr;
};
Last edited on
Thank you for your answers.


Basically I have to be very careful with Object handles and pay attention to temporary scope so I don't lose objects that I don't want to de-reference immediately in the same statement.
This problematic code
int *p = getPointer().get(); // note: no dereferencing operator
Fails to properly transfer unique ownership from the temporary unique_ptr. p becomes a dangling pointer on the next line.

The member function get() exists for the express purpose of avoiding the ownership transfer. Calling it on an rvalue (which might soon be destroyed) is potentially a mistake. If you really are worried about this, you can forbid it if you want, but realize this is not always wrong:
1
2
pointer get() const& noexcept { return data; };
pointer get() const&& noexcept = delete;


The correct way to write the above is
unique_ptr<int> p = getPointer();
Note that no copy of the resource is made here: p now owns the resource pointed to by the result of getPointer().
Last edited on
Topic archived. No new replies allowed.