Лучший способ объявить другом шаблон класса, не зная его аргументов - PullRequest
0 голосов
/ 11 февраля 2019

Я наткнулся на сценарий, и я пытаюсь найти самый чистый подход, если таковой имеется.

У меня есть шаблонный класс с защищенным конструктором, который должен быть создан в другом шаблоне.учебный класс.Оба разделяют часть параметров шаблона, но не все.Вот пример моей проблемы.

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

Решение 1 - Я передаю «ненужные» параметры шаблона классу с защищенным конструктором (класс Element).

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 elem{tp};
        container_.emplace_back(elem);        
    }
};

Решение 2 - Iиспользовать подход, подобный тому, который я нашел здесь Как объявить шаблонную структуру / класс своим другом?

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 elem{tp};
        container_.emplace_back(elem);        
    }
};

1 Ответ

0 голосов
/ 11 февраля 2019

У вас все еще есть несколько вариантов для изучения.

  1. Вы можете сделать один класс внутренним классом (называемым вложенным классом), который автоматически добавит его в класс "outside".См. https://en.cppreference.com/w/cpp/language/nested_types

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

По запросу от OP, вот схема одного из способов реализации 2. option: (используя 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");
    }
};
...