move contructor and move assignment for a union-like class

Is there a better way to write a move constructor and move assignment for the class below.

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
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
// g++ -std=c++14 main.cpp Token.cpp -o z -Wall -pedantic
#include <string>

class Token
{
public:
	// copy control needed because our class has a unioin with a string
	Token() : tok(INT), ival{0} { }
	Token(const Token &t) : tok(t.tok) { copyUnion(t); }
	Token(Token &&);
	Token &operator=(const Token&);
	Token &operator=(Token&&);

	// if the union holds a string, we have to destroy it
	~Token();

	Token &operator=(const std::string&);
	Token &operator=(char);
	Token &operator=(int);
	Token &operator=(double);
private:
	enum {INT, CHAR, DBL, STR} tok;
	union
	{
		char 	cval;
		int 	ival;
		double 	dval;
		std::string sval;
	}; // each Token object has an unnamed member of this unnamed union type
	// check the discriminant and copy the union member as appropriate
	void copyUnion(const Token&);
};


Token &Token::operator=(const std::string &s)
{
	if (tok == STR)
		sval = s;
	else
		new(&sval) std::string(s);

	tok = STR;
	return *this;
}

Token &Token::operator=(char c)
{
	using std::string;
	
	if (tok == STR)
		sval.~string();	// if we have a string, free it

	cval = c;			// assign to the appropriate member
	tok = CHAR;			// update the discriminant
	return *this;
}

Token &Token::operator=(int i)
{
	using std::string;
	
	if (tok == STR)
		sval.~string();	// if we have a string, free it

	ival = i;			// assign to the appropriate member
	tok = INT;			// update the discriminant
	return *this;
}

Token &Token::operator=(double d)
{
	using std::string;
	
	if (tok == STR)
		sval.~string(); 	// if we have a string, free it

	dval = d;			// assign to the appropriate member
	tok = DBL;			// update the discriminant
	return *this;
}


void Token::copyUnion(const Token &t)
{
	switch (t.tok)
	{
		case Token::INT:
			ival = t.ival;
			break;
		case Token::CHAR:
			cval = t.cval;
			break;
		case Token::DBL:
			dval = t.dval;
			break;
		case Token::STR:
			new(&sval) std::string(t.sval);
			break;
	}
}

// ### move contructor ###
Token::Token(Token &&t) : tok(std::move(t.tok))
{
	switch(t.tok)
	{
		case Token::INT:
			ival = t.ival;
			t.ival = 0;
			break;
		case Token::CHAR:
			cval = t.cval;
			t.cval = 0;
			break;
		case Token::DBL:
			dval = t.dval;
			t.dval = 0;
			break;
		case Token::STR:
			new(&sval) std::string(t.sval);
			break;
	}
}

Token &Token::operator=(const Token &t)
{
	using std::string;

	if (tok == STR && t.tok != STR) // if this object holds a string and t doesn't,
		sval.~string();				// we have to free the old string

	if (tok == STR && t.tok == STR)
		sval = t.sval;
	else
		copyUnion(t);
	tok = t.tok;
	return *this;
}

// ### move assignment ###
Token &Token::operator=(Token &&t)
{
	using std::string;

	if (tok == STR && t.tok != STR) // if this object holds a string and t doesn't,
		sval.~string();			// we have to free the old string

	if (tok == STR && t.tok == STR)
		sval = t.sval;
	else
		copyUnion(t);

	tok = std::move(t.tok);
	return *this;
}



Token::~Token()
{
	using std::string;
	if (tok == STR)
		sval.~string();
}

int main()
{
	Token t;
	t = 15.198;
	t = 'a';
	t = "Hello";
	t = 5;
	t = 'a';
	t = "Hello";
	t = 15.198;
	return 0;
}
Last edited on
This is one way (caveat: untested 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
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
#include <iostream>
#include <string>
#include <stdexcept>
#include <algorithm>
#include <new>

namespace utility
{
    struct int_or_str
    {
        struct type_error : public virtual std::domain_error { type_error() : std::domain_error("wrong type") {} };
        using string = std::string ;

        operator int&() { if( type == INT ) return i ; else throw type_error() ; }
        operator int() const { if( type == INT ) return i ; else throw type_error() ; }
        operator string&() { if( type == STR ) return s ; else throw type_error() ; }
        operator string() const { if( type == STR ) return s ; else throw type_error() ; }

        int_or_str() = default ;
        int_or_str( int v ) : type(INT), i(v) {}
        int_or_str( const string& v ) : type(STR), s(v) {}
        int_or_str( string&& v ) : type(STR), s( std::move(v) ) {}
        int_or_str( const char* v ) : type(STR), s(v) {}

        int_or_str( const int_or_str& that ) : type(that.type)
        { if(type==INT) i = that.i ; else new ( std::addressof(s) ) string(that.s) ; }

        int_or_str( int_or_str&& that ) : type(that.type)
        { if(type==INT) i = that.i ; else new ( std::addressof(s) ) string( std::move(that.s) ) ; }

        // dual-role assignment operator: both copy assignment and move assignment
        // pass rhs by value: lvalues are copied, and rvalues are moved
        int_or_str& operator= ( int_or_str that ) { swap(that) ; return *this ; }

        ~int_or_str() { if( type == STR ) s.~string() ; }

        void swap( int_or_str& that )
        {
            using std::swap ;

            if( type == INT )
            {
                if( that.type == INT ) swap( i, that.i ) ;
                else
                {
                    const int saved_i = i ;
                    new ( std::addressof(s) ) string( std::move(that.s) ) ;
                    that.s.~string() ;
                    that.i = saved_i ;
                }
            }

            else // type == STR
            {
                if( that.type == STR ) swap( s, that.s ) ;
                else
                {
                    const int saved_i = that.i ;
                    new ( std::addressof(that.s) ) string( std::move(s) ) ;
                    s.~string() ;
                    i = saved_i ;
                }
            }

            swap( type, that.type ) ;
        }

        private:
            enum type_tag { INT, STR } ;
            type_tag type = INT ;
            union
            {
                int i = 0 ;
                string s ;
            };

         friend std::ostream& operator<< ( std::ostream& stm, const int_or_str& x )
         { if( x.type == int_or_str::INT ) return stm << x.i ; else return stm << x.s ; }

         friend bool operator== ( const int_or_str& a, const int_or_str& b )
         {
             if( a.type != b.type ) return false ;
             else return a.type == int_or_str::INT ? ( a.i == b.i ) : ( a.s == b.s ) ;
         }
         friend bool operator!= ( const int_or_str& a, const int_or_str& b ) { return !(a==b) ; }

         // etc.
    };

    void swap( int_or_str& a, int_or_str& b ) { a.swap(b) ; }
}

int main()
{
    utility::int_or_str x = 45 ;
    utility::int_or_str y = "hello world" ;
    std::cout << x << ' ' << y << '\n' ;

    x = "hello again" ;
    y = 92 ;
    std::cout << x << ' ' << y << '\n' ;

    std::string s = x ; int& ri = y ; std::cout << s << ' ' << ri << '\n' ;

    ri += 123 ;
    std::cout << x << ' ' << y << '\n' ;

    using std::swap ;
    swap( x, y ) ;
    std::cout << x << ' ' << y << '\n' ;
}

http://coliru.stacked-crooked.com/a/3e9a6cf3c9fc3db5
@JLBorges Thank you, hope I've done it right this time.

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
Token::Token(Token &&t) : tok(std::move(t.tok))
{
	switch(t.tok)
	{
		case Token::INT:
			ival = t.ival;
			break;
		case Token::CHAR:
			cval = t.cval;
			break;
		case Token::DBL:
			dval = t.dval;
			break;
		case Token::STR:
			new(&t.sval) std::string(std::move(t.sval)) ;
			break;
		case Token::SDA:
			new(&t.sdaval) Sales_data(std::move(t.sdaval)) ;
			break;
	}
}

Token &Token::operator=(Token &&t)
{
	using std::string;

	if (tok == STR && t.tok != STR) // if this object holds a string and t doesn't,
		sval.~string();				// we have to free the old string

	if (tok == SDA && t.tok != SDA)	// if this object holds a Sales_data and t doesn't,
		sdaval.~Sales_data();		// we have to free the old Sales_data

	if (tok == STR && t.tok == STR)
		sval = t.sval;
	else if (tok == SDA && t.tok == SDA)
		sdaval = t.sdaval;
	else
	{
		switch(t.tok)
		{
			case Token::INT:
				ival = t.ival;
				break;
			case Token::CHAR:
				cval = t.cval;
				break;
			case Token::DBL:
				dval = t.dval;
				break;
			case Token::STR:
				new(&t.sval) std::string(std::move(t.sval)) ;
				break;
			case Token::SDA:
				new(&t.sdaval) Sales_data(std::move(t.sdaval)) ;
				break;
		}
		
		tok = std::move(t.tok);
	}

	return *this;
}
Last edited on
Topic archived. No new replies allowed.