模板函数

时间:2017-10-16 21:32:19

标签: c++ templates dependencies circular-dependency

我有一个带有以下声明的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 ++不喜欢它。

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

    编辑:在实际案例中,不仅有一个子类B,而是几个(B,C,D等)函数A :: doThat()取决于所有他们。函数B :: doThis()也是模板化的。

4 个答案:

答案 0 :(得分:1)

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>();
}

答案 1 :(得分:1)

通常,允许父级调用子函数的最佳方法是将该函数声明为父级中的纯虚函数,并在子级中将其覆盖。

#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>();
}

答案 2 :(得分:0)

以下内容适用于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中(或任何名称)你喜欢)。

答案 3 :(得分:0)

它呼吁多态性。使用多态性有两种选择:

  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>(); }
};