C ++:模板参数循环依赖

时间:2010-01-26 22:55:37

标签: c++ templates coding-style circular-dependency

这本身就是一个最佳实践问题,而不是一个语言问题,因为我已经有了解决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> { /* ... */ };

但是这会带来一系列问题,因为sAsB确实不是AB。例如,我无法调用A的构造函数而没有将它们正确地复制到sA中,或者以某种方式在代码周围闪烁出来。

我的问题是:处理这个问题的最佳实用方法是什么?这个问题有什么特别聪明的解决方案吗?

我正在使用MSVC2008和G ++,但欢迎使用具有编译器特定扩展名的解决方案。

谢谢,

阿列克

2 个答案:

答案 0 :(得分:2)

如上所述,处理此问题的最佳实用方法是重构 - 通过解耦来打破依赖关系。

可能的选项包括:

  • 使用虚拟方法进行接口
  • 静态接口(可能使用类型特征或概念检查)
  • 在一侧或两侧使用回调(可通过仿函数或信号缓解)

只要您的要求突然改变,这也可以帮助您。假设在某些情况下你需要一个特殊的server - 它当然应该支持你已经写过的所有客户端,你不想重写它们。或者在某些情况下你需要一些特殊的client ...... 使用你的方法,这需要重写双方,只需要一个解耦的方法,只需要编写你需要改变的方面的修改版本。

以此为例与客户的静态方法:

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&)或兼容的东西,编译就会失败。
如果你想要更正式,你可以查看静态断言和概念检查。

答案 1 :(得分:0)

由于模板的类型为其所有参数命名,因此无法进行无限循环的参数化。

您可能(当然)只是试图同时向相反方向发送信息。这没有问题,但您无法将信息封装在提供实现的类中。

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 { … };