C ++: циклическая зависимость параметра шаблона - PullRequest
0 голосов
/ 27 января 2010

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

Я имею дело с типичной проблемой циклической зависимости при замене параметров шаблона. У меня есть следующая пара классов:

template<class X>
class A { /* ... */ };

template<class X>
class B { /* ... */ };

и я хочу создать каждый экземпляр следующим образом:

// Pseudocode -- not valid C++.
A<B> a;
B<A> b;

то есть я хочу «связать» A с B, а B с A.

Я могу решить проблему, грубо говоря, с помощью предварительной декларации с уловкой наследования:

class sA;
class sB;

class sA : public A<sB> { /* ... */ };
class sB : public B<sA> { /* ... */ };

но это порождает ряд проблем, поскольку sA и sB на самом деле не A и B. Например, я не могу вызвать конструкторы A, не продублировав их должным образом в sA или не свернув каким-либо образом код вокруг кода.

Мой вопрос: каков наилучший практический способ решения этой проблемы? Какое-нибудь особенно умное решение этой проблемы?

Я использую MSVC2008 и G ++, но приветствуются решения с расширениями, специфичными для компилятора.

Спасибо

Alek

Ответы [ 2 ]

2 голосов
/ 27 января 2010

Как уже упоминалось, лучший практический способ справиться с этим - это рефакторинг - разделить зависимости, развязав их.

Возможные варианты:

  • интерфейсы с использованием виртуальных методов
  • статические интерфейсы (возможно, с использованием признаков типа или концептуальных проверок)
  • с использованием обратных вызовов с одной или обеих сторон (возможно, с помощью функторов или сигналов)

Это также поможет вам, когда ваши требования внезапно изменятся. Предположим, вам нужен специальный server в некоторых случаях - он, конечно, должен поддерживать всех клиентов, которых вы уже написали, вы не хотите их переписывать. Или вам нужны специальные client s в некоторых случаях ...
При вашем подходе это потребует переписывания обеих сторон, тогда как в случае несвязанного подхода это всего лишь вопрос написания модифицированной версии стороны, которую нужно изменить.

Принимая, например, статический подход с клиентами:

template<class server>
class client {
    server& srv;
public:
    client(server& srv) : srv(srv) {};
    void work(const request& req) {
        srv.add_job(make_job(req));
    }
};

Здесь client даже не нужно знать конкретный тип server - если у него нет функции-члена add_job(job&) или чего-то совместимого, компиляция просто не удастся.
Если вы хотите стать более формальным, вы можете посмотреть на статические утверждения и концептуальные проверки.

0 голосов
/ 27 января 2010

Поскольку тип шаблона называет все его параметры, вы не можете иметь бесконечный цикл параметризации.

Возможно, вы (наверняка) просто пытаетесь отправить информацию в противоположных направлениях одновременно. С этим проблем нет, но вы не можете инкапсулировать информацию в классах, обеспечивающих реализацию.

template< class W > // define an abstract class to pass data
struct widget_traits {};

template<>
struct widget_traits< SpringySpring > { // specialize to put data in it
    struct properties { … };
    enum { boing = 3 };
};

template< class V >
struct veeblfetzer_traits {};

template<>
struct veeblfetzer_traits< VonNeumann > {
    typedef int potrzebie;
};

template< struct WT, struct VT > // pass info by using as argument
struct MyWidget { … };

template< struct VT, struct WT > // both ways
struct MyVeeblfetzer { … };
...