我确实在这里被问过: 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;并且两者都相互依赖作为模板参数。
在不使用任何虚拟类(接口)的情况下,解决此类模板参数的最佳方法是什么?
提前致谢。
答案 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;
当然,这只能解决您所说的问题。但真正的教训是:尝试考虑您期望的内存布局,这将帮助您弄清楚如何在代码中表达它。