代码之家  ›  专栏  ›  技术社区  ›  Igor

如何在模板类中专门化赋值运算符?

  •  3
  • Igor  · 技术社区  · 8 年前

    我想用自定义赋值运算符编写一个聚合模板结构,如下所示:

    template <typename T>
    struct Foo {
        Foo() = default;
        Foo(const Foo&) = default;
    
        Foo& operator=(const Foo& f) { ... }
    
        ...
    };
    

    现在,如果 T 是我想要的const限定类型:

    Foo& operator=(const Foo& f) = delete;
    

    我唯一能想到的办法就是专攻 Foo 结构:

    template<T> struct Foo<const T> {
       Foo& operator=(const Foo& f) = delete;
       ... a lot of code ...
    }
    

    但是为了专门化这个结构,我必须复制粘贴所有剩余的代码(聚合意味着没有继承——至少在C++ 17之前,不可能将公共代码移动到基类)。

    有没有更好的办法?

    2 回复  |  直到 8 年前
        1
  •  7
  •   max66    8 年前

    我建议一种自继承:从泛型版本继承的const专门化

    template <typename T>
    struct Foo
     {
       Foo() = default;
       Foo(Foo const &) = default;
    
       Foo& operator= (Foo const & f) { return *this; }
     };
    
    template <typename T>
    struct Foo<T const> : public Foo<T>
     {
       Foo& operator= (Foo const & f) = delete;
     };
    

    这样,您的const专门化就继承了泛型版本的所有内容,因此不需要复制和传递所有通用代码,除了 operator=() 已删除。

    下面是一个完整的例子

    template <typename T>
    struct Foo
     {
       Foo() = default;
       Foo(Foo const &) = default;
    
       Foo& operator= (Foo const & f) { return *this; }
     };
    
    template <typename T>
    struct Foo<T const> : public Foo<T>
     {
       Foo& operator=(Foo const & f) = delete;
     };
    
    int main () 
     {
       Foo<int>        fi;
       Foo<int const>  fic;
    
       fi  = fi;   // compile
       // fic = fic; // compilation error
     }
    
        2
  •  2
  •   jxh    8 年前

    我想你可以用CRTP完全隐藏任务。在结构上,它类似于 self-inheritance technique ,但它使用静态多态性在基中实现赋值运算符。这个 const 专用版本被删除,因此调用赋值运算符的尝试将失败。

    template <typename D>
    struct FooCRTP {
        D & derived () { return *static_cast<D *>(this); }
        D & operator = (const D &rhs) { return derived() = rhs; }
    };
    
    template <typename T> struct Foo : FooCRTP<Foo<T>> {};
    
    template <typename T>
    struct Foo<const T> : FooCRTP<Foo<const T>>
    {
        Foo & operator = (const Foo &) = delete;
    };
    

    与自继承技术相比,CRTP版本的一个优点是,在CRTP解决方案中,基类赋值是使用派生的赋值实现的。然而,在自继承技术中,基类赋值是它自己的实现,因此它可能会被意外调用。例如:

    Foo<int> f_int;
    Foo<const int> f_cint;
    
    f_cint.Foo<int>::operator=(f_int);
    

    上述代码将无法用CRTP编译,但编译器不会抱怨使用了自继承技术。