【问题标题】:Same classes with different template argument cannot access private fields of each other具有不同模板参数的相同类不能访问彼此的私有字段
【发布时间】:2014-12-11 22:22:26
【问题描述】:

我有一个模板类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
        }
}

有什么技巧可以让它成为可能吗?也许我可以用不同的模板参数定义一个朋友类?

【问题讨论】:

    标签: c++ templates friend


    【解决方案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());
    }
    

    【讨论】:

      【解决方案2】:

      您可以通过在linked_ptr&lt;&gt; 的模板类主体中使用以下声明来使各种不同类型的linked_ptr&lt;&gt; 成为彼此的朋友:

      template <class> friend class linked_ptr;
      

      但是,这样做会面临试图分配给不兼容指针的问题。也就是说,如果U 是与T 不同的类型,那么linked_ptr&lt;U&gt; * 将是与linked_ptr&lt;T&gt; * 不同的类型,更明显的是,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,但它实际上是完整模板类型的别名(填充了模板参数)。

      【讨论】:

      • 只有当 U 是 T 的孩子时才有效。有没有办法做到这一点?
      猜你喜欢
      • 2015-01-28
      • 1970-01-01
      • 1970-01-01
      • 2019-12-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多