Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Overload copy assignment operator for a member struct of a non-type template struct

I have the following non-type template:

template<size_t MAX_SIZE>
struct Path{
    struct Point{
        float x;
        float y;
       }
    };
    Point segment[MAX_SIZE];
};

If I now declare two different Paths, I cannot assign elements of the different segments to each other, as the structs may have the same structure, but are of different type :

Path<10> path_a ;
Path<30> path_b ;
path_a.segment[0].x = 1;
path_a.segment[0].y = 2;
path_b.segment[0] = path_a.segment[0]; // <- error C2679 in Visual Studio)

Of course, if I separate the definition of Point and Path, the assignment would work:

struct Point{
        float x;
        float y;
       };

template<size_t MAX_SIZE>
struct Path{
    Point segment[MAX_SIZE];
};

But that's not what I want (this is just a MWE), so I was wondering how I can overload the copy assignment operator to make it work. I've tried numerous variants, for example:

template<size_t MAX_SIZE>
struct Path{
    struct Point{
        float x;
        float y;
        template<size_t OTHER_SIZE>
        Point & operator = (const typename Path<OTHER_SIZE>::Point & that)
        {
            x = that.x;
            y = that.y;
            return *this;
        }
    };
    Point segment[MAX_SIZE];
};

but I always get the same error. So my question is: Is it possible to overload = in a way that allows for an assignment of the following form without changing the layout of my structs?

path_b.segment[0] = path_a.segment[0];
like image 287
magnetometer Avatar asked Apr 06 '16 14:04

magnetometer


People also ask

How do you overload an assignment operator?

Overloading the assignment operator (operator=) is fairly straightforward, with one specific caveat that we'll get to. The assignment operator must be overloaded as a member function. This will call f1. operator=(f1), and under the simplistic implementation above, all of the members will be assigned to themselves.

What does copy assignment operator do in C++?

A trivial copy assignment operator makes a copy of the object representation as if by std::memmove. All data types compatible with the C language (POD types) are trivially copy-assignable.

When copy constructor is called and when assignment operator is called?

In a simple words, Copy constructor is called when a new object is created from an existing object, as a copy of the existing object. And assignment operator is called when an already initialized object is assigned a new value from another existing object.

What is the difference between assignment operator and copy constructor in C++?

The Copy constructor and the assignment operators are used to initializing one object to another object. The main difference between them is that the copy constructor creates a separate memory block for the new object. But the assignment operator does not make new memory space.


1 Answers

Yes, such setup is possible. At the core, you need an assignment operator template which will accept all types:

template<class T>
Point & operator = (const T & that)

As a basic solution, this would be enough. It will now work with all types which have members x and y of compatible types, and produce a (usually) ugly error message for types which don't.

If that's good enough for you, we're done.

If you have other overloads of the assignment operator, you will probably want to selectively disable the template one. For this, you will need to instrument the Point classes and use SFINAE:

template<size_t MAX_SIZE>
struct Path{
    struct Point{
        float x;
        float y;
        struct EnableAssignment {};
    };
    Point segment[MAX_SIZE];
};

The instrumentation is then used like this:

template<class T, class U = typename T::EnableAssignment>
Point & operator = (const T & that)

[Simplified live example]


The code above uses a default template argument in a function template, which was only introduced in C++11. Before that, you would have to invoke SFINAE in some other way:

template <class L, class R>
struct SfinaeThenRight
{
  typedef R type;
};

template <class T>
typename SfinaeThenRight<typename T::EnableAssignment, Point&>::type operator = (const T & that)

[Simplified C++98 live example]

like image 52
Angew is no longer proud of SO Avatar answered Sep 28 '22 10:09

Angew is no longer proud of SO