具有模板函数的循环依赖关系

Circular dependency with template function

本文关键字:循环 依赖 关系 函数      更新时间:2023-10-16

我有一个具有以下声明的类a(a.h文件):

#ifndef __A_DEFINED__
#define __A_DEFINED__
class A
{
public:
template<typename T> inline void doThat() const;
};
#endif

以及从该类派生的类B(B.h文件):

#ifndef __B_DEFINED__
#define __B_DEFINED__
#include <iostream>
#include "A.h"
class B : public A
{
public:
void doThis() const { std::cout << "do this!" << std::endl; }
};
#endif

到目前为止,一切都很好。我的问题是函数A::doThat()使用B::doThis():

template<typename T> inline void A::doThat() const { B b; b.doThis(); }

通常,循环依赖关系不会成为问题,因为我只会在.cpp文件中定义A::doThat()。然而,在我的例子中,doThat是一个模板函数,所以我不能这么做。

以下是我迄今为止设想的解决方案:

  1. 在.cpp文件中定义模板函数A::doThat()。问题是,我需要用各种模板参数显式实例化所有调用(在实际情况下可能有很多)。

  2. 在A.h中声明A类之后,添加#include "B.h",然后定义A::doThat()函数。这在visual studio中运行良好,但g++不喜欢它。

有没有一种巧妙的方法来解决这个问题?

EDIT:在实际情况中,不仅有一个子类B,还有多个子类(B、C、D等)。函数A::doThat()依赖于所有子类。函数B::doThis()也是模板化的。

B类的默认模板参数可以工作:

#include <iostream>
// include A.h
class B;
class A
{
public:
template<typename T, typename U = B> inline void doThat() const 
{
U b; b.doThis();
}
};
// include B.h
class B : public A
{
public:
void doThis() const { std::cout << "do this!" << std::endl; }
};
// main
int main()
{
A a;
a.doThat<int>();
}

通常,允许父函数调用子函数的最佳方法是在父函数中将该函数声明为纯虚拟函数,并在子函数中重写它。

#include <iostream>
class A
{
public:
virtual ~A() = default;
template<typename T> inline void doThat() const
{
// do some other stuff
doThis();
}
virtual void doThis() const = 0; // pure virtual function
};
class B: public A
{
public:
void doThis() const override
{
std::cout << "do this!" << std::endl;
}
};
int main()
{
B b;
A* ap = &b;
ap->doThat<int>();
}

以下内容适用于g++:

文件A.h:

#ifndef __A_DEFINED__
#define __A_DEFINED__
class A
{
public:
template<typename T> inline void doThat() const;
};
#include "B.h"
template<typename T> inline void A::doThat() const { B b; b.doThis(); }
#endif

文件B.h:

#include <iostream>
#include "A.h"
// We check for the include guard and set it AFTER the inclusion of A.h
// to make sure that B.h is completely included from A.h again.
// Otherwise the definition of A::doThat() would cause a compiler error
// when a program includes B.h without having included A.h before.
#ifndef __B_DEFINED__
#define __B_DEFINED__
class B : public A
{
public:
void doThis() const { std::cout << "do this!" << std::endl; }
};
#endif

文件test_A.cpp:

// In this test case we directly include and use only A.
#include "A.h"
#include "A.h" // We test whether multiple inclusion causes trouble.
int main() {
A a;
a.doThat<int>();
}

文件test_B.cpp:

// In this test case we directly include and use only B.
#include "B.h"
#include "B.h" // We test whether multiple inclusion causes trouble.
int main() {
B b;
b.doThat<int>();
b.doThis();
}

替代方案:

我不知道你(或一些编码约定)是否坚持为每个类单独的头文件,但如果不是,以下应该有效:

您可以将class Aclass B以及成员函数模板A::doThat<typename>()的定义(按此顺序)放在一个头文件AandB.h(或您喜欢的任何名称)中。

这需要多态性。使用多态性有两种选择:

  1. 动态多态性,即使A成为抽象基类,并虚拟调用doThis()

    struct A
    {
    virtual void do_this() const = 0;
    template<typename T>
    void doThat() const { doThis(); }
    };
    struct B : A
    {
    void doThis() const override { /* ... */ }
    };
    

    当然,只有在doThis()没有模板化的情况下,这才有效。如果你需要,你可以使用

  2. 静态多态性,即CRTP,当

    template<typename Derived>
    struct A
    {
    template<typename T>
    void doThat() const { static_cast<const Derived*>(this)->template doThis<T>(); }
    };
    struct B : A<B>
    {
    template<typename T>
    void doThis() const { /* ... */ }
    };
    

如果(在您的示例代码中)B::doThis()不是为同一个对象调用的,而是为某个临时对象调用,则可以

template<typename typeB>
struct A
{
template<typename T>
void doThat() const { typeB b; b.template doThis<T>(); }
};