2014-12-11 1 views
1

У меня есть класс шаблона my_class<T>, который получает аргумент конструктора my_class<U> (как правило, с другим параметром шаблона). В отличие от кода без шаблонов или с тем же параметром шаблона, я не могу получить доступ к приватным полям экземпляра my_class<U>, тем не менее это тот же класс.Те же классы с различным аргументом шаблона не могут получить доступ к приватным полям друг друга

template <class T> 
class my_class { 

    private:    
     T * data; 

    public: 
     template <class U> 
     my_class(const my_class<U> & p) { 
      data = p.data; // <-- visibility exposing 
     } 
} 

Есть ли уловка, чтобы сделать это возможным? Может быть, я могу определить класс друга с другим аргументом шаблона?

ответ

1

Чтобы ответить на ваш комментарий: «Он должен работать только в том случае, если U - это ребенок T. Есть ли способ сделать это?». Используйте черты. Это поможет вам определить, получен ли U из T ..

С ниже черты, следующие допустимые аргументы функции, которые используют их:

/** 
    Arguments can be (is_non_class_same<T, U>::value): 
     T = T  -> U = T for any class. 
     T = void* -> U = any non-class pointer. 
     T = int* -> U = int*. U cannot be a float*. Incompatible pointers. 
     T = int -> U = float 
     T = double -> U = float or int. Valid.. double is large enough to hold float and int. 
     T = int -> U = double Invalid.. double is larger than type float.. loss of precision. 
     T = float -> U = double Invalid.. double is larger than type float.. loss of precision. 

    Arguments can be (is_class_same<T, U>::value, U>::type): 

     T = Base* -> U = Base*. 
     T = Base* -> U = Derived* 
     T = Base -> U = Derived Invalid.. See: Slicing Problem. 
**/ 

Пример (рабочий код): http://ideone.com/dHZhHc < - живой пример ..

#include <type_traits> 
#include <memory> 
#include <iostream> 

template<typename T, typename U> 
struct is_non_class_same : public std::integral_constant<bool, (!std::is_base_of< 
     typename std::remove_pointer<T>::type, 
     typename std::remove_pointer<U>::type>::value && 
     std::is_convertible<U, T>::value && sizeof(T) >= sizeof(U)) || 
(std::is_class<T>::value && std::is_class<U>::value && 
!std::is_pointer<T>::value && !std::is_pointer<U>::value && 
std::is_same<T, U>::value)> 
{}; 

template<typename T, typename U> 
struct is_class_same : public std::integral_constant<bool, std::is_base_of< 
     typename std::remove_pointer<T>::type, 
     typename std::remove_pointer<U>::type>::value && 
     std::is_pointer<T>::value> 
{}; 


class Node 
{ 
protected: 
    Node* previous; 
    Node* next; 

    Node() : previous(nullptr), next(nullptr) {} 

    template<class T, class TD> 
    friend class linked_ptr; 

public: 
    virtual ~Node() {} 

}; 

template<typename T, typename TD = typename std::remove_pointer<T>::type> 
class linked_ptr : public Node 
{ 
private: 
    template<class, class> friend class linked_ptr; /** Access friend level **/ 
    T data; 

public: 

    template<class U, typename TU = typename std::remove_pointer<U>::type, typename = typename std::enable_if<is_non_class_same<T, U>::value || is_class_same<T, U>::value, U>::type> 
    linked_ptr(U data) : data(data) {} 
    ~linked_ptr(); 

    template<class U, typename TU = typename std::remove_pointer<U>::type, typename = typename std::enable_if<is_non_class_same<T, U>::value || is_class_same<T, U>::value, U>::type> 
    void setData(U data) 
    { 
     this->data = data; 
    } 

    template<class U, typename TU = typename std::remove_pointer<U>::type, typename = typename std::enable_if<is_non_class_same<T, U>::value || is_class_same<T, U>::value, U>::type> 
    void append(U data); 
}; 

template<typename T, typename TD> 
linked_ptr<T, TD>::~linked_ptr() 
{ 
    TD(data); 
    delete this->next; 
} 

template<typename T, typename TD> 
template<typename U, typename TU, class> 
void linked_ptr<T, TD>::append(U data) 
{ 
    if (!this->next) 
    { 
     this->next = new linked_ptr<U>(data); 
     this->next->previous = this; 
     return; 
    } 

    Node* t = this->next; 
    while(t->next != nullptr) 
    { 
     t = t->next; 
    } 

    t->next = new linked_ptr<U>(data); 
} 




class foo 
{ 
public: 
    virtual ~foo() 
    { 
     std::cout<<"destroyed foo\n"; 
    }; 

    virtual void print() 
    { 
     std::cout<<"foo\n"; 
    } 
}; 

class bar : public foo 
{ 
public: 
    virtual ~bar() 
    { 
     std::cout<<"destroyed bar\n"; 
    } 

    virtual void print() 
    { 
     std::cout<<"bar\n"; 
    } 
}; 

int main() 
{ 
    linked_ptr<foo*> list(new foo()); 
    list.append(new bar()); 
    list.append(new bar()); 
    list.append(new foo()); 
} 
1

Вы можете сделать все различные виды linked_ptr<> друзей друг от друга с помощью следующего объявления в классе шаблон тела linked_ptr<>:

template <class> friend class linked_ptr; 

Однако, делая это, вы столкнулись с проблемой, что вы пытаетесь назначить несовместимые указатели. То есть, если U отличается от T, то linked_ptr<U> * будет отличаться от linked_ptr<T> *, и, более очевидно, U * будет отличаться от T *.

синтаксис
data = p.data;   // error: assigning U* to T* 
    next = p.next;   // error: assigning linked_ptr<U>* to linked_ptr<T>* 
    ... 

C++ позволяет использовать неукрашенный linked_ptr в классе, но это на самом деле псевдоним для полного типа шаблона (с параметрами шаблона, заполненных).

+0

Он должен работать только в том случае, если U является ребенком T. Есть ли способ сделать это? –

Смежные вопросы