【问题标题】:C++, circular dependencies, templates and user typesC++、循环依赖、模板和用户类型
【发布时间】:2016-06-17 13:29:39
【问题描述】:

我遇到了 A、B、C 类之间的循环依赖问题。类 A 中的用户类型 cfunction 指向 C::F1 的静态方法。代码如下:

文件 A.h

#ifndef A_H
#define A_H

#include "C.h"
class C;

template <typename T> using cfunction = T(*)(T, T);

template <typename T>
class A{
    public:
        cfunction <T> X;
        A () : X(&C::F1) {}
        A (const cfunction <T> &pX ) : X(pX){};
        virtual ~A() = 0;   
};
#endif

文件 B.h

#ifndef B_H
#define B_H
#include "A.h"

template <typename T>
class B : public A <T> {
  public:
   B() : A<T>(), b(0.0) {}
   B(const T b_, const cfunction <T> &pX ) : A <T>(pX), b(b_){}
   virtual ~B() {};
};
#endif

最后,在 C 的方法 init() 中存储了指向 A 的共享指针。方法 F1 使用模板参数 F3 调用 F2。代码如下:

文件 C.h

#ifndef C_H
#define C_H

#include "A.h"
template <typename T>
class A;

#include <memory>
#include <list>

template <typename T>
using List = std::list<std::shared_ptr<A<T> > >;

//Definition of all projections
class C  {
    public:
      template <typename T, typename Function> static T F2(Function f, const T a, const T b);
      template <typename T> static void init(List<T> l);
      template <typename T> static T F1(const T a, const T b);
      template <typename T> static T F3(const T a, const T b);
};
#include "C.hpp"
#endif

文件 C.hpp

#ifndef C_HPP
#define C_HPP

#include "B.h"
template <typename T>
class B;

template <typename T, typename Function> 
T C::F2(Function f, const T a, const T b) { return  f(a, b);}

template <typename T> void C::init(List<T> l) {
    auto test = std::make_shared <B < T >> (0.0, F1<T>);
    l.push_back(test);
}

template <typename T> T C::F1(const T a, const T b) {  return F2(F3<T>, a, b);}
template <typename T> T C::F3(const T a, const T b) {return a + b;}

#endif

主文件:main.cpp

#include "C.h"

int main(){
    List <double> l;
    C::init(l);
    return 0;
}

抱歉,代码有点复杂。更简单的代码版本运行良好,但这种“完整”变体很有效。我无法解决 g++ 的问题;编译选项:-std=c++11。

感谢您的帮助...

【问题讨论】:

  • 不要同时使用 #include 和 forward-declare(在事后进行前向声明是没有意义的)。由于您不需要在“C.h”中定义A,因此将其#include 移动到“C.hpp”。因为你需要在“C.hpp”中定义B,所以去掉它的声明。

标签: c++ function templates pointers circular-dependency


【解决方案1】:

好的,您的问题可以通过一些小的调整来解决。正如您所指出的,目前您有一些循环依赖项,但只需进行 1 个基本且稍有修改即可打破它们:即删除引用 CA 默认构造函数。您实际上并不需要它 - 因为它代表您的代码不使用它。即使您这样做了,您也可以将X 成员设置为nullptr 并稍后在外部对其进行初始化。

删除后,您现在可以有一个简单的包含顺序:A.hB.hC.hC.hpp

在那之后我必须修复其他几个编译器错误:您似乎正在启动一个不存在的B 成员b。此外,即使您的 A 析构函数是纯虚拟的,它也需要一个定义。最终代码如下:

EDIT(2): 我现在已经修改了这个,所以A 的默认构造函数不再被排除在外。在C 的定义可用之后,它只是稍后在C.h 中定义。

啊哈:

#ifndef A_H
#define A_H

//#include "C.h"
//class C;

template <typename T> using cfunction = T(*)(T, T); 

template <typename T>
class A{
    public:
        cfunction <T> X;
        //A () : X(&C::F1) {}
        A ();
        A (const cfunction <T> &pX ) : X(pX){};
        virtual ~A() = 0;
};

template <typename T>
A<T>::~A() {}

#endif

B.h:

#ifndef B_H
#define B_H
#include "A.h"

template <typename T>
class B : public A <T> {
  public:
   B() : A<T>() //, b(0.0) 
   {}  
   B(const T b_, const cfunction <T> &pX ) : A <T>(pX) //, b(b_)
   {}  
   virtual ~B() {}; 
};
#endif

C.h:

#ifndef C_H
#define C_H

#include "A.h"
#include "B.h"

//template <typename T>
//class A;

#include <memory>
#include <list>

template <typename T>
using List = std::list<std::shared_ptr<A<T> > >;

//Definition of all projections
class C  {
    public:
      template <typename T, typename Function> static T F2(Function f, const T a, const T b); 
      template <typename T> static void init(List<T> l); 
      template <typename T> static T F1(const T a, const T b); 
      template <typename T> static T F3(const T a, const T b); 
};

template<typename T>
A<T>::A() : X(&C::F1)
{}

#include "C.hpp"
#endif

C.hpp:

#ifndef C_HPP
#define C_HPP

//#include "B.h"
//template <typename T>
//class B;

template <typename T, typename Function> 
T C::F2(Function f, const T a, const T b) { return  f(a, b);}

template <typename T> void C::init(List<T> l) {
    auto test = std::make_shared <B < T >> (0.0, F1<T>);
    l.push_back(test);
}

template <typename T> T C::F1(const T a, const T b) {  return F2(F3<T>, a, b);}
template <typename T> T C::F3(const T a, const T b) {return a + b;} 

#endif

【讨论】:

  • @Smeetheey:谢谢;但是如果需要 X(&C::F1) {} 怎么办?这是一个简化的例子,上面提到的初始化很重要……
  • 解释为什么需要它?你的意思是你需要A 的默认构造函数吗?如果是这样,我建议在所述构造函数中将X 设置为nullptr,然后添加一个类似void setX(cfunction&lt;T&gt; val) { X = val; } 的函数,然后从外部调用它。否则,您能否进一步说明您为什么需要它?
  • 是一个比较复杂的模型,其中B不是一个单一的类。从 A 派生的类有 20 多个(B 就是一个例子)。进行“两次”初始化是不舒服的。就我的问题而言,我也很感兴趣如何完全解决这个问题(不仅仅是简化版本)。我花了大部分时间解决这个问题:-)
  • 实际上我现在已经编辑了,所以你可以恢复原来的默认构造函数,查看编辑。诀窍是使用在A 中声明但仅在C 可用后才定义的静态成员变量
  • @Smeeheey:非常感谢您的解决方案,您真是太好了。我不想浪费你的时间和精力......但是,有没有任何静态变量的解决方案?
【解决方案2】:

B.h 需要包含 A.h(因为它需要知道它的细节)

A.h 需要包含 C.h(因为它需要知道它的细节)。通过这样做,它不需要前向声明 C,因为它已经通过 C.h 的包含知道它

但是,从您的代码中,C.h 不需要了解 A 的细节,因此不需要包含 A.h。相反,它可以简单地向前声明 A,因为它已经在做

类似地,C.hpp 不需要包含 B,因为它也不需要知道 B 的任何细节。一个简单的前向声明(就像你已经在做的一样)就足够了

任何时候你看到自己包含一个文件并同时前向声明你从该文件中获得的一个类表明你有一些事情要做

【讨论】:

  • @Altainia:不幸的是,它对我不起作用:Ah(#include "Ch")、Bh(#include "Ah)、Ch(template class A;) 和C.hpp(template class B;)。它并不像看起来那么简单。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2012-03-09
  • 1970-01-01
  • 1970-01-01
  • 2018-09-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多