Assignment operator but with some member exceptions

The task is to use the assignment operator of a class, but change all the data except certain ones. For example, below we are to assign all Person data of 'other' except for 'name' and 'ID':

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
#include <iostream>
#include <string>

struct Person {
	std::string name;
	int ID, age, height, weight;
	Person (const std::string& n, int a, int b, int c, int d) : name(n), ID(a), age(b), height(c), weight(d) {}
	struct ExclusionData {
		std::string name;
		int ID;
		ExclusionData (const Person& person) : name(person.name), ID(person.ID) {}
	};
	void assignWithExceptions (const Person& other) {
		const ExclusionData data(*this);
		*this = other;
		name = data.name;
		ID = data.ID;
	}
	void print() const {
		std::cout << "Name = " << name << '\n';
		std::cout << "ID = " << ID << '\n';
		std::cout << "Age = " << age << '\n';
		std::cout << "Height = " << height << '\n';
		std::cout << "Weight = " << weight << "\n\n";
	}
};


int main() {
	Person bob ("Bob", 2047, 38, 183, 170);
	Person frank ("Frank", 5025, 25, 190, 205);
	bob.print();
	frank.print();

	std::cout << "Bob pretends to be Frank, but keeps his name and ID.\n";
	bob.assignWithExceptions (frank);
	bob.print();
}


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Name = Bob
ID = 2047
Age = 38
Height = 183
Weight = 170

Name = Frank
ID = 5025
Age = 25
Height = 190
Weight = 205

Bob pretends to be Frank, but keeps his name and ID.
Name = Bob
ID = 2047
Age = 25
Height = 190
Weight = 205


But I think the way I did it is pretty lousy (note the wasted steps changing the name and ID only to revert them back?) and would like to hear some better ideas, which are more elegant, while being maintainable in the event that the choice for the exclusions change as the program evolves. The maintainability is the only thing I somewhat like about my method, though that is still not ideal either (ideally any changes should be made in just one place instead of two).

So the ideal solution should require no wasted steps, unlike the method above, and changes to what the exclusions should be should be in only one place (not two like above). Of course, we assume that Person shall have many, many data members (and constantly increasing), so that simply defining Person::operator= (const Person& other) to handle all data except for 'name' and 'ID' is out of the question.
Last edited on
Copy the non-excluded parts into a temporary, and then move those into the object?

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
struct Person {

    std::string name;
    int ID, height, weight;
    std::string age ; // **** changed to std::string to illustrate move
    Person (const std::string& n, int id, std::string age, int h, int w )
        : name(n), ID(id), height(h), weight(w), age(age)  {}

    struct not_excluded_part_of {

        int height, weight;
        std::string age ;
        not_excluded_part_of( const Person& other ) // copy the non-excluded part
            : height(other.height), weight(other.weight), age(other.age) {} // copy string age
        not_excluded_part_of( Person&& other )
            : height(other.height), weight(other.weight), age( std::move(other.age) ) {} // move string age
    };

    Person& operator= ( not_excluded_part_of&& ne ) // move-assign the copy of the non-excluded part
    { std::swap(age,ne.age) ; std::swap(height,ne.height) ; std::swap(weight,ne.weight) ; return *this ; } // move string age

    Person& assignWithExceptions( const Person& other ) { return *this = not_excluded_part_of(other) ; }
    Person& assignWithExceptions( Person&& other ) { return *this = not_excluded_part_of( std::move(other) ) ; }

    // ...

};

http://coliru.stacked-crooked.com/a/76c25bd2ca9279db
Last edited on
Perfect efficiency there for certain. So every time a new data member of Person is added, 'struct not_excluded_part_of' needs to be updated too (which is easy to forget)? There is no good solution that will avoid this maintenance responsibility? No method with variadic templates or pointers to data members or whatever other technique that can just specify which data members are excluded, with that being the only part to maintain?

Ok, following up on your idea, what about this as a solution?
Performance: Optimum.
Maintenance: Ideal I think, since any new data members added to Person::NE needs only be made there and nowhere else. New data members added in Person but outside of Person::NE are irrelevant to the problem at hand.
Elegance: I'm not sure of this, since Person now has a funny-looking data member 'NE data;'.

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
#include <iostream>
#include <string>

struct Person {
    struct NE {
        std::string age;
        int height, weight;
        NE (const std::string& a, int h, int w) : age(a), height(h), weight(w) {}
        NE(const Person& other)
            : age(other.getAge()), height(other.getHeight()), weight(other.getWeight()) {}
        NE (Person&& other)
            : age(std::move(other.getAge())), height(other.getHeight()), weight(other.getWeight()) {}
    };
    std::string name;
    int ID;
    NE data;  // *** Holds all data of Person that is to be reassigned with operator=.
    
    Person (const std::string& n, int id, const std::string& a, int h, int w) :
		name(n), ID(id), data(NE(a,h,w)) {}  // Member initializer uses NE(a,h,w) for 'data'.

    Person& operator= (NE&& ne) {
		std::swap(data, ne);  return *this;  // No changes need to be made here when NE gets new data members added.
	}
	
    Person& assignWithExceptions (const Person& other) {return *this = NE(other);}
    Person& assignWithExceptions (Person&& other) {return *this = NE(std::move(other));}

	std::string getAge() const {return data.age;}
	int getHeight() const {return data.height;}
	int getWeight() const {return data.weight;}

	void print() const {
		std::cout << "Name = " << name << '\n';
		std::cout << "ID = " << ID << '\n';
		std::cout << "Age = " << getAge() << '\n';
		std::cout << "Height = " << getHeight() << '\n';
		std::cout << "Weight = " << getWeight() << "\n\n";
	}
};

int main() {
	Person bob ("Bob", 2047, "38 years and 9 months", 183, 170);
	Person frank ("Frank", 5025, "25 years and 2 months", 190, 205);
	bob.print();
	frank.print();

	std::cout << "Bob pretends to be Frank, but keeps his name and ID.\n";
	bob.assignWithExceptions (frank);
	bob.print();
}


Output:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Name = Bob
ID = 2047
Age = 38 years and 9 months
Height = 183
Weight = 170

Name = Frank
ID = 5025
Age = 25 years and 2 months
Height = 190
Weight = 205

Bob pretends to be Frank, but keeps his name and ID.
Name = Bob
ID = 2047
Age = 25 years and 2 months
Height = 190
Weight = 205
Last edited on
Factor into two classes:

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
namespace detail
{
    struct person_identity
    {
        std::string name;
        int ID ;
        person_identity( const std::string& n, int id ) : name(n), ID(id) {}
    };

    struct person_attributes
    {
        std::string age ;
        int height, weight;
        person_attributes( std::string age, int h, int w ) : age(age), height(h), weight(w) {}
    };
}

struct Person : private detail::person_identity, private detail::person_attributes {

    Person (const std::string& n, int id, std::string age, int h, int w )
        : detail::person_identity(n,id), detail::person_attributes(age,h,w)  {}

    Person& assignWithExceptions( const Person& other ) { attributes() = other ; return *this ; }
    Person& assignWithExceptions( Person&& other ) { assign_attributes( std::move(other) ) ; return *this ; }

    // ...

    private:
        void assign_attributes( person_attributes&& attrib ) { std::swap( attributes(), attrib ) ; }
        detail::person_attributes& attributes() { return static_cast<detail::person_attributes&>(*this) ; }
};

http://coliru.stacked-crooked.com/a/682c638ade1682c2
Topic archived. No new replies allowed.