模板参数循环依赖

Template Parameter Circular Dependency

我确实看到有人在这里问过这个问题: 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;两者作为模板参数相互依赖。

在不使用任何虚拟 class(接口)的情况下解析这种模板参数的最佳方法是什么?

提前致谢。

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

如果接受此依赖项,请考虑大致的内存布局:

// 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.
      }
    }
  }
}

相反,让我们换个角度思考:如果我们只需要将一个消费者和一个生产者连接在一起,那么它们是同一数据结构的一部分才有意义。我们可以使用 Curiously Recurring Template Pattern 来避免生产者中的模板参数:

#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;

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