Templates,pointers and move constructor

I have created a pair using templates but I have come across an interesting dilemma,what if we have a pair which has a pointer instead of an int or double or string or a user defined type. in the move constructor we need to keep the pointer in a valid state so lets say if we steal the data which is being pointed to by the temporary or rvalue object,we need to set that rvalues pointer to be a nullptr or else our first or second in the pair will point a nullptr.

thanks


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
68
69
70
71
72
#include <iostream>

using namespace std;

template <class T,class U>
class Pair{

  public:
      T first;
      U second;

      Pair(){}
      Pair(T first,T second)
      :first(first),second(second)
      {}
      Pair(const Pair<T,U> &other){

          first = other.first;
          second = other.second;
      }
      Pair(const Pair<T,U> &&other){

         first = other.first;
         second = other.second;
      }
      Pair<T,U>& operator=(Pair<T,U> other){

           swap(*this,other);
           return *this;
      }
      bool operator==(const Pair<T,U> other)const{

          return first == other.first && second == other.second;
      }
      bool operator!=(const Pair<T,U> other)const{

          return first != other.first && second != other.second;
      }
      bool operator<(const Pair<T,U> other)const{

          return first < other.first && second < other.second;
      }
      bool operator>(const Pair<T,U> other)const{

          return first > other.first && second > other.second;
      }


      void swap(Pair& a,Pair& b){

        using std::swap;
        swap(a.first,b.first);
        swap(a.second,b.second);

      }
};


int main()
{
    pair<int,string> a(1,"a");
    pair<int,string> b;
    pair<int,string> c = pair<int,string>(3,"c");
    // above line calls move cons,but lets say if instead of string we had a pointer to a string so U would be 
    // second in the pair,how can we check for a pointer so we can leave the pointer in a valid state in the move cons??
    b = a;
    if(a == b){

        cout << "both pairs seem to be the same" << endl;
    }
}
I think you are overthinking it. Moving a pointer normally leaves it unchanged. I don't understand why you would want to do anything different inside your Pair.

Note that your move constructor is actually copying. If you want it to move you need to make the rvalue-reference non-const and use std::move.

1
2
3
4
Pair(Pair &&other){
	first = std::move(other.first);
	second = std::move(other.second);
}

But this is just what the default-defined move-constructor would do so you don't really need to define it explicitly. The same is true for the copy constructor and the copy-assignment operator.
Last edited on
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
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
#include <iostream>
#include <type_traits>

using namespace std;


template <class T,class U>
class Pair{

  public:
      T first;
      U second;

      Pair(){}
      Pair(T first,T second)
      :first(first),second(second)
      {}
      Pair(const Pair<T,U> &other){

          if(is_pointer<T>::value){

             first = new T(other.first);
          }
          else{

             first = other.first;
          }
          if(is_pointer<U>::value){

            second = new U(other.second);
          }
          second = other.second;
      }
      Pair(const Pair<T,U> &&other){

         if(is_pointer<T>::value){

             cout << "first is a pointer" << endl;
             first = other.first;
             other.first = nullptr;
         }else{

             first = other.first;
         }
         if(is_pointer<U>::value){

             cout << "second is a pointer" << endl;
             second = other.second;
             other.second = nullptr;

         }else{

            second = other.second;
         }
      }
      Pair<T,U>& operator=(Pair<T,U> other){

           swap(*this,other);
           return *this;
      }
      bool operator==(const Pair<T,U> other)const{

          return first == other.first && second == other.second;
      }
      bool operator!=(const Pair<T,U> other)const{

          return first != other.first && second != other.second;
      }
      bool operator<(const Pair<T,U> other)const{

          return first < other.first && second < other.second;
      }
      bool operator>(const Pair<T,U> other)const{

          return first > other.first && second > other.second;
      }


      void swap(Pair& a,Pair& b){

        using std::swap;
        swap(a.first,b.first);
        swap(a.second,b.second);

      }
};


int main()
{
    pair<int,string> a(1,"a");
    pair<int,string> b;
    pair<int,string> c = pair<int,string>(3,"c");
    // above line calls move cons,but lets say if instead of string we had a pointer to a string so U would be
    // second in the pair,how can we check for a pointer so we can leave the pointer in a valid state in the move cons??
    b = a;
    if(a == b){

        cout << "both pairs seem to be the same" << endl;
    }

    string adam = "adam";
    string steve = "steve";
    pair<int,string*> p_a(26,&adam);
    pair<int,string*> p_b = pair<int,string*>(25,&steve);
    pair<int,string*> p_c;
    p_c = p_b;

    cout << p_b.second << endl;
    cout << p_c.second << endl;

    if(p_b == p_c){

        cout << "YES!" << endl;
    }else{

       cout << "NO!!!!" << endl;
    }
}


hi Peter I came up with that solution above but the problem is it copies the memory address not the pointer,

also good point I could just use std::move but another problem would be in the copy constructor where I want to create a new first or second(depending which one is the pointer)

any idea how I could do it?

thanks
Last edited on
Note that you are using std::pair inside main(). If you want to use your class template Pair you need to spell it with uppercase P.
Note that you are using std::pair inside main(). If you want to use your class template Pair you need to spell it with uppercase P.


wow didn't even notice that :o thanks Peter,my code was running fine but it must have been calling std::pair

now none of my code is working :/

C:\Users\User\TemplatePairs\main.cpp|91|error: invalid conversion from 'const char*' to 'int' [-fpermissive]|

now I am getting this error on pretty much every line I use my Pair class
The constructor takes two T and no U.

1
2
3
Pair(T first,T second)
             ^
             You mean U here, right?
yes indeed,I think I need to take a break from my computer screen for a while haha
but before I leave

I am still getting User\TemplatePairs\main.cpp|39|error: cannot convert 'std::nullptr_t' to 'int' in assignment error

If you use a regular if statement the code inside still needs to be valid even if the condition is always false. To solve this issue you can use constexpr if.

 
if constexpr (is_pointer<T>::value) {

http://en.cppreference.com/w/cpp/language/if#Constexpr_If


Another problem is that if T is a pointer, then new T will create a new pointer and return a pointer to that pointer. I guess you will have to use std::remove_pointer (or something equivalent) in order to get the type that T points to.

 
first = new std::remove_pointer_t<T>(*other.first);

https://en.cppreference.com/w/cpp/types/remove_pointer
Last edited on
If you use a regular if statement the code inside still needs to be valid even if the condition is always false. To solve this issue you can use constexpr if.


that's new to me :O still don't understand how the code wouldn't be valid if the condition was always false

also I never knew if consexpr was even a thing I guess it must have been introduced in c++11
but if this was only introduced in c++11 how did programmers overcome this before it was introduced?

and it seems like I can't include an else statement after if constexpr any reason why?
I tried changing the code to

1
2
3
4
5
6
7
8
9
10
11

if constexpr ( is_pointer<T>::value){

             cout << "first is a pointer" << endl;
             first = other.first;
             other.first = nullptr;
         }else{

             first = other.first;
         }


but get an error |35|error: expected '(' before 'constexpr'|

thanks
still don't understand how the code wouldn't be valid if the condition was always false

C++ is not a scripting language. The code needs to make sense for the types involved when it is being compiled.

The problem in this particular situation was that std::remove_pointer is not defined for non-pointer types, and that other.first might not have a dereference operator.

I never knew if consexpr was even a thing I guess it must have been introduced in c++11

It was introduced in C++17.

if this was only introduced in c++11 how did programmers overcome this before it was introduced?

Before C++17 I think you normally used SFINAE for this sort of thing. https://en.cppreference.com/w/cpp/language/sfinae

EDIT: In this situation I'm not sure if SFINAE would have been overkill though. You can probably accomplish the same thing using overloaded function templates that you call from the constructor. I'm not sure which approach is best. Both are way more complicated than using constexpr if anyhow.

it seems like I can't include an else statement after if constexpr any reason why?
I tried changing the code [...] but get an error |35|error: expected '(' before 'constexpr'|

Looks like you're not using C++17.
Last edited on
Hi Peter,

no I'm using c++11

Topic archived. No new replies allowed.