Need help with move constructor/assignment operations

I am completely lost in move constructor/assignment functionality.

I am writing Pointer class for Arduino, where I don't have access to std cast/move operations.

Here is the code:

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
template<typename T>
class Pointer {
    T *pointer;
public:
    Pointer(T *pointer) : pointer(pointer) {}

    Pointer(Pointer &&pointerToMove) {
        std::cout << "Move constructor" << std::endl;
        pointer = pointerToMove.pointer;
        pointerToMove.pointer = nullptr;
    }

    Pointer &operator=(Pointer &&pointerToMove) {
        std::cout << "Move assignment" << std::endl;
        pointer = pointerToMove.pointer;
        pointerToMove.pointer = nullptr;
    }

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

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


void takePointer(Pointer<int> pointer) {
    //do something
}

int main() {
    Pointer<int> pointer1 = {new int{7}};
    Pointer<int> pointer2 = pointer1; //compilation error 1

    takePointer(pointer1); //compilation error 2

    takePointer({new int{7}}); // is move constructor invoked here or value is copied?

    return 0;
}


I got a compilation error "call to implicitly-deleted copy constructor of 'Pointer<int>'". I created move constructor/assignment. As I understand pointer1 is not an rvalue in error 1 and 2, is there any way to make it rvalue (without using std)?

Is move constructor actually invoked when takePointer function is called? If yes, why don't I see my log line?

In general I want to be able to safely create pointer inside method and move it around.


Last edited on
I am writing Pointer class for Arduino, where I don't have access to std cast/move operations


Maybe you saw the top of this snippet?
http://www.cplusplus.com/forum/general/231043/#msg1043754
Of course it wouldn't help you, given that it was wrong (I didn't write the word struct.)

1
2
3
4
5
6
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); }


If static_cast is not available, use a C-style cast instead:
1
2
3
template <typename T>
constexpr typename remove_reference<T>::type&& move(T&& x) noexcept
{ return (typename remove_reference<T>::type&&) x; }

Although static_cast isn't in the standard namespace, it's a language builtin.

Using it leads to code like this:
http://coliru.stacked-crooked.com/a/033dc553c9c1d0e6
Yeah, that time you scared me.

I will be brave now.

Could you explain what is going on here? As I am progressing in The C++ Programming Language 4th Edition by Bjarne Stroustrup, I haven't got to template programming yet.

Last edited on
If you don't understand the implementation of move() for now, it's not a problem. You can treat it as a black box - you don't need to know how it works to use it properly.

Anyways I'm not certain I can explain this completely without an idea of how much C++ you're familiar with. move() in particular is really complicated.

There are 2 parts to the above. The first is the class template remove_reference. For any type T, the type typename remove_reference<T>::type is just T without any reference qualifiers on it.

For example, if T is an lvalue reference type, like int&, then typename remove_reference<T>::type is int. If T is an rvalue reference type, like double&&, then the type typename remove_reference<T>::type is double. Otherwise, if T is any other type like char const* or void(int), then typename remove_reference<T>::type is char const* or void(int), respectively.

remove_reference is an example of a meta-function. It relies on template partial specialization. That's part of chapter 25 in TC++PL.
http://en.cppreference.com/w/cpp/language/partial_specialization

The second part is move() itself.
1
2
3
template <typename T>
constexpr typename remove_reference<T>::type&& move(T&& x) noexcept
{ return (typename remove_reference<T>::type&&) x; }

It relies on the implicit creation of reference-to-references, and a bunch of subtleties about template argument deduction.

Thomas Becker explains it much better than I can, in this well-known article:
http://thbecker.net/articles/rvalue_references/section_01.html

But before you read that, you might watch this talk:
https://channel9.msdn.com/Shows/Going+Deep/Cpp-and-Beyond-2012-Scott-Meyers-Universal-References-in-Cpp11
and/or read this:
https://isocpp.org/blog/2012/11/universal-references-in-c11-scott-meyers
With the minor caveat that the invented term "universal references" are actually called "forwarding references".

Last edited on
Thanks a lot for this explanation.
Topic archived. No new replies allowed.