Цикл с несколькими параметрами шаблона - PullRequest
0 голосов
/ 12 декабря 2018

У меня есть два класса, класс B имеет член A1, а класс A1 имеет ссылочный член B, это легко написать так:

class B;

class A1 {
 public:
  A1(const B& b) : b_(b) {}
 private:
  const B& b_;  // a reference to B
};

class B {
 public:
  B(A1 a1) : a1_(a1) {}
 private:
  A1 a1_;  // a Type A1 member 
};

А затем мне нужно изменить класс B на класс шаблона, поэтому код должен выглядеть следующим образом:

template<typename T1>
class B;


class A1 {
 public:
  A1(const B<A1> &b) : b_(b) {};
 private:
  const B<A1> &b_; // a reference to B<A1>
};

template<typename T1>
class B {
 public:
  B(T1 t1) : t1_(t1){}
 private:
  T1 t1_; //a T1 type member
};

И вдруг нам нужен второй член в B, поэтому я изменил B к этому:

template<typename T1, typename T2>
class B {
 public:
  B(T1 t1, T2 t2) : t1_(t1), t2_(t2){}
 private:
  T1 t1_;
  T2 t2_;
};

Это вызвало проблему:

теперь, если я кодировщик A, приведенный ниже код недопустим:

template<typename T1, typename T2>
class B;


class A1 {
 public:
  A1(const B<A1> &b) : b_(b) {}; //wrong, need a second template argument
 private:
  const B<A1> &b_; // wrong, either
};

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

template <typename T1, typename T2>
class B;

template <typename TB>
class A1 {
 public:
  explicit A1(const TB& b) : b_(b) {}
 private:
  const TB& b_;
};

template <typename TB>
class A2 {
 public:
  explicit A2(const TB& b) : b_(b) {}
 private:
  const TB& b_;
};

template <typename T1, typename T2>
class B {
 public:
  B(T1 a1, T2 a2) : a1_(a1), a2_(a2) {}
 private:
  T1 a1_;
  T2 a2_;
};

Это выглядит хорошо, за исключением того, что я не знаю, как сделать экземпляр B (например: B<A1<B<A1<...>,A2>, A2>, это рекурсивно.)

Идеальный дизайн, который я с нетерпением жду, заключается в том, что программистам для A1 и A2 просто не нужно знать друг друга, а программисту для B просто нужно добавить A1 и A2 больше илименее похоже на это B<A1, A2> (или добавьте еще кое-что), и даже если он добавит параметр T3, такой как B<A1, A2, A3>, the code of A1 and A2`, изменять его не нужно.

Итак

1: Если я настаиваю на этом синтаксисе, означает ли это, что я должен отказаться от использования шаблона для B, вместо этого использовать указатели?

2: Если я настаиваю на использовании шаблона, означает ли это, что класс T1 и класс T2 должны знать друг друга?

3: существует ли третий способ использования шаблона приT1 и T2 независимы?То есть независимо от того, сколько параметров я добавляю в класс B, класс A1 менять не нужно?Шаблон template / CRTP / SFINAE, все это кажется бесполезным.Поскольку класс B является только ссылкой в ​​классе A, класс B больше похож на интерфейс, чем на определенный класс, это напоминает мне о предложении C ++ «концепция», может ли «концепция» помочь в этомcase?

4: Это проблема xy?Это означает, что я не должен разрабатывать свой шаблон кода таким образом?

Ответы [ 2 ]

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

Вот как это можно сделать.

// B doesn't know about A
template <class T1, class T2>
class B {};

// A doesn't know about B
template <template<class> class TB>
class A {
    using MyB = TB<A<TB>>;
};

template <class K>
    using Bint = B<K, int>;

int main() {
    A<Bint> abint;
}

Редактировать: Если B имеет более одного параметра шаблона, это также можно сделать, хотя и немного сложнее.Нужно связать все параметры шаблона в одном классе, используя несколько помощников.Вот пример.(Я переименовал классы для ясности).

// The top class parameterised by 3 bottom classes
template <class A1, class A2, class A3>
struct Top {};

// Three bottom classes parameterised by the top
template <template<class> class T> struct Bottom1 {};
template <template<class> class T> struct Bottom2 {};
template <template<class> class T> struct Bottom3 {};

// Until this point, none of the classes know about any other.
// Now tie them together with these helper definitions.

template <typename K>
using BundledTop = Top<typename K::A1, typename K::A2, typename K::A3>;

struct Bundle
{
    using A1 = Bottom1<BundledTop>;
    using A2 = Bottom2<BundledTop>;
    using A3 = Bottom3<BundledTop>;
};

using MyTop = BundledTop<Bundle>;
0 голосов
/ 13 декабря 2018

Я бы сказал, что ваш ответ 4 является правильным: это выглядит как некорректный дизайн.Я не могу доказать это, но я хотел бы привести вам пример.

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

Но почему класс ветвей должен хранить листья, созданные для произвольных ветвей (потому что Bтип является параметром шаблона A)?Если B хранит экземпляры A, то я не могу думать о какой-либо причине для того, чтобы сделать тип B параметром шаблона для A.

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

С нетерпением жду ваших комментариев.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...