在不知道其模板参数的情况下将类模板声明为朋友的最佳方法

Posted

技术标签:

【中文标题】在不知道其模板参数的情况下将类模板声明为朋友的最佳方法【英文标题】:Best approach to declare as friend a class template without knowing one its template arguments 【发布时间】:2019-02-11 12:07:09 【问题描述】:

我偶然发现了一个场景,我正在尝试找出最干净的方法,如果有的话。

我有一个带有受保护构造函数的模板类,它需要由朋友模板类实例化。两者共享部分模板参数,但不是全部。这是我的问题的一个例子。

我想从有经验的程序员那里知道是否有其他可能的解决方案(我想没有,除了将构造函数公开),如果在这两者之间,我提出的一个比另一个更容易接受。 谢谢

解决方案 1- 我向具有受保护构造函数(元素类)的类提供“不必要的”模板参数。

template <typename Tp_>
class Engine_Type_X

;
template <typename Tp_>
class Engine_Type_Z

;

//Forward declaration
template <typename Tp_, template<typename> typename Eng_>
class Container;

//Eng_ is only required to declare the friend class
template <typename Tp_,template<typename> typename Eng_> 
class Element

    friend class Container<Tp_,Eng_>;

    Tp_ tp_;
protected:  
    Element(Tp_ tp) : tp_tp   //protected ctor!!!
    
;

template <typename Tp_, template<typename> typename Eng_>
class Container

    using Element_tp = Element<Tp_,Eng_>;
    using Engine_tp  = Eng_<Tp_>;

    std::vector<Element_tp> container_;
    Engine_tp               &engine_;

public:
    Container(Engine_tp &engine) : container_,engine_engine
    

    void install(Tp_ tp)
       Element_tp elemtp;
        container_.emplace_back(elem);        
    
;

解决方案 2 - 我使用的方法类似于我在此处找到的方法 How to declare a templated struct/class as a friend?

template <typename Tp_>
class Engine_Type_X

;
template <typename Tp_>
class Engine_Type_Z

;

template <typename Tp_>
class Element

    template<typename,template<typename>typename> friend class Container; //All templated classes are friend

    Tp_ tp_;
protected:  
    Element(Tp_ tp) : tp_tp   //protected ctor!!!
    
;

template <typename Tp_, template<typename> typename Eng_>
class Container

    using Element_tp = Element<Tp_>;
    using Engine_tp  = Eng_<Tp_>;

    std::vector<Element_tp> container_;
    Engine_tp               &engine_;

public:
    Container(Engine_tp &engine) : container_,engine_engine
    

    void install(Tp_ tp)
       Element_tp elemtp;
        container_.emplace_back(elem);        
    
;

【问题讨论】:

两者有不同的含义。在第一个中只有Container&lt;Tp_,Eng_&gt;;Element&lt;Tp_,Eng_&gt; 的朋友在第二个中任何Container 是任何Element 的朋友,选择你需要的。 partial template specialization for friend classes?的可能重复 在您的情况下,另一种选择可能是将Element 声明为Container 的内部类(不确定这将如何适合您的整体设计)。 使用第二个。 Engine 逻辑上是 Container 的详细信息,因此将其放在 Element 的签名中是没有意义的。 【参考方案1】:

您仍有一些选择。

    您可以将一个类设为内部类(称为嵌套类),这样会自动 将它与“外部”班级交朋友。见https://en.cppreference.com/w/cpp/language/nested_types

    另一种方法是要求一个所谓的“令牌”作为参数 构造函数,这个token类型一般不带模板参数,那么就让这个token只能是 由其他类创建(可以是嵌套类型或友元类型)。

应 OP 的要求,这里概述了完成 2. 选项的一种方法:(使用 c++0x)

template<typename Test, template<typename...> class Ref>
struct is_specialization : std::false_type ;

template<template<typename...> class Ref, typename... Args>
struct is_specialization<Ref<Args...>, Ref>: std::true_type ;


template <class T>
class create_token 
    public:
    typedef T Type;
                 //copy of token not allowed !
    create_token(const create_token &) = delete; 
    create_token& operator=(const create_token &) = delete; 
                 //move is fine
    create_token(create_token &&) = default; 
    create_token& operator=(create_token &&) = default; 

    friend class T;
    private:
    create_token();
  ;


template<class BlaBlaBla>
struct Element 
    template<class T>
    Element(create_token<T> t)  
        static_assert(std::is_specialization<create_token<T>::Type, Container>::value, "Wrong type of token provided");
    
;

template<class Whatever>
struct Container 
    template<class T>
    Element(create_token<T> t)  
        static_assert(std::is_specialization<create_token<T>::Type, Element>::value, "Wrong type of token provided");
    
;

【讨论】:

如果我将 Element 嵌套在 Container 中,如何在不明确将 Container 声明为朋友的情况下访问其受保护的构造函数?我不明白你的第二个建议。你能否提供一个简单的例子,不一定基于我自己的。谢谢。 @JacintoResende 我概述了如何执行第二个选项。它可能包含错误等,但我希望你觉得它有用。

以上是关于在不知道其模板参数的情况下将类模板声明为朋友的最佳方法的主要内容,如果未能解决你的问题,请参考以下文章

如何在不丢失 css 的情况下将模板从桌面发送到移动设备

如何在不使用画布的情况下将整个 div 数据转换为图像并将其保存到目录中?

如何在不使用全局存储的情况下将类对象从一个屏幕传递到另一个屏幕

在不知道编码类型的情况下将 NSData 转换为 NSString

如何在不知道其宽度的情况下将 ul li 列表居中? (分页)

如何在不采用其中更改的情况下将 git 分支声明为合并?