【问题标题】:Calling a template method from another template method in C++?从 C++ 中的另一个模板方法调用模板方法?
【发布时间】:2014-08-25 20:30:17
【问题描述】:

我目前遇到了模板化方法的问题。我有这个实现模板方法的公共类:

namespace Private { class InternalClass; }

namespace Public
{
    class PublicClass
    {
    public:
        PublicClass();
        virtual ~PublicClass();

        template<class T>
        bool Add(bool primary);

    private:
        Private::InternalClass* _pInternal;
    };

    template<class T>
    bool PublicClass::Add(bool primary) { return _pInternal->Add<T>(primary); }
}

内部类是这样实现的:

namespace Private
{
    class InternalClass
    {
    public:
        InternalClass();
        virtual ~InternalClass();

        template <class T>
        bool Add(bool primary);
    };

    template<class T>
    bool InternalClass::Add(bool primary) { return false; }
}

由于提供的源无法使用此内部类标头,因此我必须在 PublicClass 标头中对其进行类转发,并将包含添加到 PublicClass.cpp 文件中的 PrivateClass.h 中。

1) 知道为什么我会收到以下错误:

错误:成员访问不完整类型“Private::InternalClass”/注意:转发>“Private::InternalClass”声明

2) 隐藏我的 PublicClass::Add() 实现的最佳方法是什么?

更新

如 Cornstalks 所述,错误 1) 的原因是 this

对于 2),如何在 PublicClass 头文件中不包含 PrivateClass.h 的情况下隐藏我的实现?

【问题讨论】:

  • 您知道您的模板将专门用于哪些类型?
  • 是的,我有一堆从 ITypeClass 派生的类,即 AClass、BClass、CClass... 我正在调用 Add(true) 或 Add(false)。我不知道有多少派生类。
  • 可以在Private定义下定义函数。
  • 您可以转发声明事物,但您不能使用转发声明类型的对象(也就是说,您不能取消引用它们或调用它们的成员函数)。编译器告诉你:你正在访问一个不完整类型的成员(你不能在_pInternal上调用Add,因为_pInternal的“真实”类型是未知的,所以编译器不知道甚至知道_pInternal有一个Add方法,也不知道怎么调用这样的成员)。
  • @Manu343726:OP 无法使用模板推导,因为函数参数只是 bool,而不是 T。模板参数必须指定。

标签: c++ templates


【解决方案1】:

您遇到了一个非常有趣的问题——您想要实现 PImpl idiom,其中私有实现的类有一个模板方法。好吧,这可以解决,这意味着您可以隐藏模板的实现,但只有当您知道哪些类型将用于在您的程序中实例化您的 Add&lt;T&gt; 方法时。

假设,您的模板仅适用于 AClassBClass 类型。然后您可以按如下方式拆分文件(cmets 是内联的):

文件 public.h

#ifndef PUBLIC_H
#define PUBLIC_H

// Forward declaration ! It's sufficient in this case !
namespace Private { class InternalClass; }

// Declare all classes your Add<T> method should work with
struct AClass {};
struct BClass {};

namespace Public
{
    class PublicClass
    {
    public:
        PublicClass() {}
        virtual ~PublicClass() {}

        template <typename T>
        bool Add(bool primary); // DO NOT implement this method, just declare

    private:
        Private::InternalClass* _pInternal;
    };

    // "Explicit instantiation declarations", for each type the method will work with:
    extern template bool PublicClass::Add<AClass>(bool primary);

    extern template bool PublicClass::Add<BClass>(bool primary);
}

#endif

文件 public.cpp

#include "public.h"

// NOTE: this is hidden in CPP file, noone will see your implementation
namespace Private
{
    class InternalClass
    {
    public:
        InternalClass() {}
        virtual ~InternalClass() {}

        template <typename T>
        bool Add(bool primary);
    };

    // Magic! Here is the actual implementation of your private method
    template <typename T>
    bool InternalClass::Add(bool primary)
    {
        return false;
    }
}

namespace Public
{
    // Original definition moved to CPP file !
    template <typename T>
    bool PublicClass::Add(bool primary)
    {
        return _pInternal->Add<T>(primary);
    }

    // And again list the allowed types, this time using "explicit instantiation definitions"
    template bool PublicClass::Add<AClass>(bool primary);

    template bool PublicClass::Add<BClass>(bool primary);
}

文件 main.cpp

#include "public.h"

int main()
{
    Public::PublicClass pc;
    pc.Add<AClass>(true); // works !
    pc.Add<BClass>(false); // works !

    // pc.Add<int>(true); linker error as expected,
    // becuase there is no explicit instantiation for Add<int>

    return 0;
}

【讨论】:

  • 谢谢皮奥特!我能够正确实施这些更改。效果很好!
猜你喜欢
  • 2014-07-06
  • 2013-03-07
  • 2016-11-16
  • 2011-05-18
  • 1970-01-01
  • 2014-03-31
  • 1970-01-01
  • 2012-12-20
  • 2012-07-05
相关资源
最近更新 更多