引用模板类型的赋值运算符需要非常量重载

时间:2018-03-24 22:56:14

标签: c++ templates reference assignment-operator

我正试图围绕一个复制赋值运算符问题。虽然我有一些想法(最后列出),但我真的很茫然。这是一个问题,因为我使用的第三方库对其类没有控制权。

假设您有一个带有复制赋值运算符的模板化容器。此运算符接受具有不同模板的另一个容器,并尝试static_cast另一种类型。

template <class U>
vec2<T>& operator=(const vec2<U>& v) {
    x = static_cast<T>(v.x);
    y = static_cast<T>(v.y);
    return *this;
}

这对于简单赋值很好,但是当使用T的引用时,会得到关于const值类型的编译错误。如果添加另一个接受非const引用的重载,它将编译并运行。

我做了一个简单的例子来帮助说明这个问题。

template <class T>
struct vec2 final {
    vec2(T x_, T y_)
            : x(x_)
            , y(y_) {
    }

    template <class U>
    vec2(const vec2<U>& v)
            : x(static_cast<T>(v.x))
            , y(static_cast<T>(v.y)) {
    }

    template <class U>
    vec2<T>& operator=(const vec2<U>& v) {
        if (this == &v)
            return *this;

        x = static_cast<T>(v.x);
        y = static_cast<T>(v.y);
        return *this;
    }

    // Fix :
    /*
    template <class U>
    vec2<T>& operator=(vec2<U>& v) {
        x = static_cast<T>(v.x);
        y = static_cast<T>(v.y);
        return *this;
    }
    */

    T x;
    T y;
};

我是如何尝试使用它的:

int main(int, char**) {
    vec2<int> v0 = { 0, 0 };
    vec2<int> v1 = { 1, 1 };
    vec2<int&> test[] = { { v0.x, v0.y }, { v1.x, v1.y } };

    vec2<int> muh_vec2 = { 2, 2 };
    test[0] = muh_vec2;
    printf("{ %d, %d }\n", test[0].x, test[0].y);

    return 0;
}

最新的AppleClang会产生以下错误:

main4.cpp:18:7: error: binding value of type 'const int' to reference to type 'int'
      drops 'const' qualifier
                x = static_cast<T>(v.x);
                    ^              ~~~
main4.cpp:63:10: note: in instantiation of function template specialization 'vec2<int
      &>::operator=<int>' requested here
        test[0] = muh_vec2;
                ^

我从中理解的是,编译器试图通过const值进行分配。但为什么并且这个问题有非侵入式解决方案?

我在这里找到了类似的问题:Template assignment operator overloading mystery

我在阅读该问题后的结论是:可能是默认的赋值运算符导致了这个问题?我仍然不明白为什么:/

以下是一个在线示例:https://wandbox.org/permlink/Fc5CERb9voCTXHiN

1 个答案:

答案 0 :(得分:1)

template <class U>
vec2<T>& operator=(const vec2<U>& v)

在此方法中,v是右侧const视图的名称。如果Uint,则v.xconst int

如果Tint&,则this->xint&

this->x = static_cast<int&>(v.x);

这显然是非法的:你不能将const int静态转换为非const引用。

一般解决方案基本上需要重建std::tuplestd::pair机制。 SFINAE可用于引导它。但一般来说,包含引用的结构和包含值的结构通常是完全不同的动物;使用一个模板两者都值得怀疑。

template <class T>
struct vec2 final {
  template<class Self,
    std::enable_if_t<std::is_same<std::decay_t<Self>, vec2>>{}, bool> =true
  >
  friend auto as_tuple( Self&& self ){
    return std::forward_as_tuple( std::forward<Self>(self).x, std::forward<Self>(self).y );
  }

然后我们可以进行SFINAE测试以确定as_tuple(LHS)=as_tuple(RHS)是否有效。

为构造这样做是另一个痛苦,因为LHS的元组类型需要按摩才能进行构造性测试。

您制作代码越通用,所需的工作就越多。在编写无限通用代码之前,请考虑实际用例。