为非类型模板结构的成员结构重载复制赋值运算符

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

我有以下非类型模板:

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

如果我现在声明两个不同的路径,我不能将不同段的元素分配给彼此,因为结构可能具有相同的结构,但类型不同:

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)

当然,如果我将点和路径的定义分开,赋值就可以了:

struct Point{
        float x;
        float y;
       };

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

但这不是我想要的(这只是一个 MWE),所以我想知道如何重载复制赋值运算符以使其工作。我尝试了多种变体,例如:

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];
};

但我总是得到同样的错误。所以我的问题是:是否可以在不更改结构布局的情况下以允许分配以下形式的方式重载 =?

path_b.segment[0] = path_a.segment[0];
template<size_t OTHER_SIZE>
Point & operator = (const typename Path<OTHER_SIZE>::Point & that)

将不起作用,因为无法推导出外部结构上的模板参数 OTHER_SIZE。您可以:

template<typename T>
Point & operator = (const T & that)
{
    x = that.x;
    y = that.y;
    return *this;
}

请注意,如果没有成员 xy 的内容被传递,您将收到编译器错误,对于这种情况应该足够了。

LIVE

是的,这样的设置是可行的。核心是,您需要一个接受所有类型的赋值运算符模板:

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

作为一个基本的解决方案,这就足够了。它现在将适用于具有兼容类型成员 xy 的所有类型,并为不具有成员的类型生成(通常)难看的错误消息。

如果这对您来说足够好,我们就完成了。

如果您有其他重载的赋值运算符,您可能希望有选择地禁用模板之一。为此,您需要检测 Point 类 并使用 SFINAE:

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

然后仪器是这样使用的:

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

[Simplified live example]


上面的代码在函数模板中使用了一个默认的模板参数,这是在 C++11 中才引入的。在此之前,您必须以其他方式调用 SFINAE:

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]