Друг неконстантной шаблонной версии класса в C ++ - PullRequest
0 голосов
/ 10 декабря 2018

Рассмотрим следующий фрагмент кода:

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

Я не хочу предоставлять способ доступа к внутреннему указателю данных в более широкий мир (даже через геттер) в моем реальном коде,Кроме того, я не делаю здесь ничего, что противоречит константности, так как я никогда не изменю правую часть уравнения.

Может быть, это абсолютно неправильный способ, нов то время это кажется совершенно разумным.

Лучше ли было бы создать бесплатную функцию?Например,

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

Этот второй подход прекрасно компилируется, но выдает ошибку ссылки (неопределенные символы), которую я до сих пор отслеживаю.

Ответы [ 3 ]

0 голосов
/ 10 декабря 2018

Вам нужно определить оператор присваивания класса и шаблонный оператор, который работает на Me<T 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;
0 голосов
/ 11 декабря 2018

Я думаю, что это немного лучше, чем принятый ответ, так как он не требует перегрузки оператора присваивания.Кроме того, эта версия отключит оператор присваивания для типов 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;
    }
0 голосов
/ 10 декабря 2018

правильный способ доступа к частному члену данных версии класса, отличающейся только константой T?

А как насчет

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

?

В вашем случае это версия T (no const), которая должна иметь доступ к личным даннымT const версия.

Таким образом, вы должны подружиться с T (нет const) версией.

Выкл. Тема: вы пометили C ++ 17, так что вы можете использоватьумные указатели.

Используйте их!

Конструктор копирования по умолчанию и operator=() по умолчанию вашего Me очень и очень опасны (двойное освобождение от той же самой выделенной памяти почти гарантировано).

...