模板参数循环依赖

时间:2015-12-11 09:56:40

标签: c++ c++11 g++

我确实在这里被问过: C++: Template Parameter Cyclic Dependency

但我不太明白那里接受的答案。

所以让我在这里重申一下这个问题。

让我说我有:

template <class P>
class Consumer {
    P m_producer;
public:
void consume(char* data, uint32_t length) {
    if (/* some error condition */) {
        m_producer.errorCallback();
    }
}
}

template <class C>
class Producer {
    C m_consumer;
void produce() {
    char* someData;
    uint32_t length;
    m_consumer.consume(someData, length);
}
}

// Pseudocode -- not valid C++.
Producer<Consumer> c;
Consumer<Producer> p;

正如您所看到的那样,Producer需要调用Consumer,而Consumer需要调用Producer;并且两者都相互依赖作为模板参数。

在不使用任何虚拟类(接口)的情况下,解决此类模板参数的最佳方法是什么?

提前致谢。

1 个答案:

答案 0 :(得分:1)

与任何这类概念问题一样,答案是:重构,重构,重构。循环模板参数依赖项表示业务逻辑中的概念错误。

如果接受的依赖项是

,请考虑内存布局大致是什么
// Not C++

Producer a {
  m_consumer {
    m_producer { // Not 'a', this is another producer.
      m_consumer { // With an also different consumer inside.
        // ... and it continues forever.
      }
    }
  }
}

相反,让我们不同地考虑一下:如果我们只需要一个消费者和一个生产者联合在一起,那么只有他们才能成为同一数据结构的一部分才有意义。我们可以使用奇怪的重复模板模式来避免生产者中的模板参数:

#include <iostream>

template<typename Producer>
class Consumer {
  Producer& p;
public:
  bool error{false};
  Consumer(Producer& p_)
      :     p(p_)
  {}
  void consume(char* data, size_t lenght) {
    if (!data) {
      p.error_callback();
      error = true;
    }
  }
};

class Producer : public Consumer<Producer>{
public:  
  Producer()
      :    Consumer<Producer>(*this)
  {}
  Consumer<Producer>& consumer() {return *this;}
  void produce() {
    char* some_data(nullptr);
    int length{0};
    consume(some_data, length);
  }
  void error_callback() {
    std::cout << "Error\n";
  }
};

int main() {
  Producer producer;
  auto& consumer = producer.consumer();
  producer.produce();
  if (consumer.error) {
    std::cout << "Got error from consumer";
  }
}

让我们考虑main()的两个第一行中的内存布局:

// Not C++
Producer a {
  consumer { // A producer implicitly has a consumer, since it inherits from it.
    reference to a;
  }
}
reference to consumer;

当然,这只能解决您所说的问题。但真正的教训是:尝试考虑您期望的内存布局,这将帮助您弄清楚如何在代码中表达它。