朋友使用C ++中的类的非const模板版本

时间:2018-12-10 03:36:51

标签: c++ templates c++17 friend

请考虑以下代码:

template<class T>
class Me {
private:
    T* data;
    size_t size;

    friend class Me<?????>;
public:
    Me(size_t s) : data(new T[s]), size(s) {}
    virtual ~Me() { delete [] data; }

    Me<T> operator= (const Me<T const>& rhs) {
        for(size_t i = 0; i < size; ++i)
            data[i] = rhs.data[i]; // Gives compile error "data is a private member of Me<XX const>"
    }
}

不管这是一个非常人为的示例,访问类的版本(仅由T来区别)的私有数据成员的正确方法是什么?如果是相反的话,我可以简单地做:friend class Me<T const>;

我不想在我的真实代码中公开一种访问内部数据指针的方法(甚至通过getter)。另外,在这里我不会做任何违反const正确性的事情,因为我永远不会修改等式的右边。

也许这完全是错误的做法,但是在当时看来这是一件非常合理的事情。

创建一个自由函数会更好吗?例如

template<class T>
Me<T> operator= (const Me<T>& lhs, const Me<T const>& rhs) {...}

第二种方法可以很好地编译,但仍给我一个链接错误(未定义符号)。

3 个答案:

答案 0 :(得分:4)

您需要定义该类的赋值运算符,并且可以在Me<T const>上使用的模板化赋值运算符还可以将no const版本赋予友谊:

template<class T>
class Me {
private:
    friend class Me<std::remove_const_t<T>>;
public:

    Me<T>& operator= (const Me<T>& rhs) {
        // ...
        return *this;
    }

    template <class U, std::enable_if_t<std::is_same_v<U, T const>, bool> = true>
    Me<T>& operator=(Me<U> const& rhs) {
        // ...
        return *this;
    }
};

Me<int> r;
Me<int const> l;
r = l;

答案 1 :(得分:3)

我认为这比公认的答案略好,因为它不需要重载赋值运算符。另外,此版本将禁用const T类型的赋值运算符。 (因为您可能始终不想启用它们)。

#include <type_traits>
template<class T>
class Me {
private:
    friend class Me<std::remove_const_t<T>>;
public:

    template <class U>
    std::enable_if_t<std::is_same<std::remove_const_t<U>, T>::value, Me<T>&>
    operator=(Me<U> const& rhs) {
        return *this;
    }
};

int main() {

    Me<int> r;
    Me<int const> l;

    r = l;
    r = r;
    l = l;

    l = r; //will cause compilation failure.
}

此版本还将支持const T类型的赋值运算符。

    template <class U>
    std::enable_if_t<std::is_same<std::remove_const_t<U>, std::remove_const_t<T>>::value, Me<T>&>
    operator=(Me<U> const& rhs) {
        return *this;
    }

答案 2 :(得分:1)

  

访问类的版本的私有数据成员的正确方法,仅区别于T的常量性?

friend class Me<std::remove_const_t<T>>;

您要使用的T版本(无const)必须访问T const版本的私有数据。

因此,您必须成为T(没有const)版本的朋友。

主题建议:您标记了C ++ 17,因此可以使用智能指针。

使用它们!

默认的复制构造函数和operator=()中的默认Me非常非常危险(几乎没有保证双重分配的相同分配内存)。