【问题标题】:Smart Pointer for a template class?模板类的智能指针?
【发布时间】:2011-09-03 02:34:10
【问题描述】:

我怀疑我不能直接使用 PIMPL 模式执行此操作。是否可以有一个指向模板类的智能指针?我无法通过转动shared_ptr 声明的旋钮进行编译。

// ============Foo.h ============
// Forward declare the implementation
template <typename T> class FooImpl;

class Foo
{
  public:
    Foo getInstance(const string& fooType);
    ...
  private:
    shared_ptr< FooImpl<T> > m_impl;
};

// ============FooImpl.h ============
template <typename T>
class FooImpl
{
    ...
};

在 Visual Studio 2008 下:“错误 C2065:'T':未声明的标识符”。我在 GCC 下收到类似的错误。 如果我取消对 FooImpl 的参数化(以便 FooTempl 继承自 FooImpl),代码将编译。

我怀疑我无法参数化智能指针,但我可能错了。

编辑:second Visual Studio 错误更能说明问题:“错误 C3203: 'FooImpl' : unspecialized class template can't be used as a template argument for template parameter 'T', expected a实型”

杰夫

【问题讨论】:

  • Tshared_ptr&lt;FooImpl&lt;T&gt;&gt; m_impl; 声明中是什么类型? Foo 应该是接受T 的模板吗?
  • @silico: T 将是 Crypto++ 对象,例如 MD5、SHA1、SHA256 等哈希值。
  • 您遇到什么错误? 总是发布编译器错误。

标签: c++ templates smart-pointers


【解决方案1】:

我不完全确定您要完成什么,但这有帮助吗?

尝试1:

// ============Foo.h ============
// Forward declare the implementation

template <typename T> class FooImpl;

template<class C>
class Foo
{
  public:
    Foo getInstance(const string& fooType);
    ...
  private:
    shared_ptr< FooImpl<C> > m_impl;
};

// ============FooImpl.h ============
template <typename T>
class FooImpl
{
    ...
};

尝试2:

// ============Foo.h ============
// Forward declare the implementation

class FooImplBase;

class Foo
{
  public:
    Foo getInstance(const string& fooType);
    ...
  private:
    shared_ptr< FooImplBase > m_impl;
};

// ============FooImpl.h ============
class FooImplBase {
  public:
    virtual void AnAPI();
    virtual int AnotherAPI();
};
template <typename T>
class FooImpl : public FooImplBase
{
    ...
};

【讨论】:

  • 最后,我无法使用带有模板参数的智能指针,我不得不切换到 3 个类(该死的几乎与上面的数字 2 完全一样)。
【解决方案2】:

您发布的代码无法编译,因为TFoo 的上下文中没有任何意义。编译器在这里需要一个名为 T 的类型,但那里不存在...不完全确定您要完成什么,但以下内容不能解决您的问题吗?

// ============Foo.h ============ 

class FooImplBase {
    virtual void WhateverFooImplIsSupposedToDo() = 0;
};

template <typename T> class FooImpl : public FooImplBase {
    T mInstance;
public:
    FooImpl(T const & pInstance) : mInstance(pInstance) {}
    virtual void WhateverFooImplIsSupposedToDo() 
    {
        // implementation which deals with instances of T
    }
}; 

class Foo 
{ 
  public: 
    Foo getInstance(const string& fooType) {
     // use m_impl->WhateverFooImplIsSupposedToDo...
    }    

    template < class T >
    Foo( T const & pInstance ) : m_impl(new FooImpl<T>(pInstance)) {}
  private: 
    shared_ptr< FooImplBase > m_impl; 
}; 

【讨论】:

  • 从概念上讲,这与我想要的相似。但是,我不希望用户必须声明 Foo&lt;int&gt; f。我不得不删除参数化的智能指针,并使用 3 个类。
  • 嗯,使用这种类型排列,您不必定义Foo&lt;int&gt;。您实际上不能,因为Foo 不是类模板...您的用户在调用站点编写Foo tTmp = 5; 之类的表达式或编写void TakeFoo(Foo const &amp; pF) 之类的函数或使用TakeFoo(123.5); 之类的表达式。这是否有意义取决于您想要实现的目标。
【解决方案3】:

您做对了,只需确保已定义 T。这在 MSVC++ 2010 上为我编译:

#include <memory>

using namespace std;

template<class T>
class Blah {
public:
    Blah() { }
};

class Foo {
public:
    shared_ptr<Blah<int>> ptr;

    Foo() : ptr(new Blah<int>()) { }
};

如果您使用的旧编译器尚未包含 C++11 的此功能,请更改

shared_ptr<Blah<int>> ptr;

shared_ptr<Blah<int> > ptr;

所以编译器不认为&gt;&gt; 是右移。不过 C++11 没有这个问题。

【讨论】:

  • 好的。让我回到它(我一直在使用 FooImpl 类的前向声明来研究 GCC)。
  • 我事先不知道我会有一个 Blah,只有一个 Blah
  • @noloader 是的,我知道,只需将 Foo 设为模板即可。
  • 我想这样做,但这妨碍了它在客户端代码中的使用。我希望用户能够做到Foo f = Foo::getInstance(...)
  • @noloader 如果Foo 是模板,则Foo f = ... 无效。对于某些T,它必须Foo&lt;T&gt; f = ...
【解决方案4】:

我事先不知道我会有一个 Blah,只有一个 Blah。

从语言的角度来看,Blah&lt;T&gt; 没有意义,因为T 不存在。根据您的具体目标,您可以

Foo也做成一个模板,这样你就可以声明一个模板参数T

template<typename T>
class Foo
{
  public:
    Foo getInstance(const string& fooType);
    ...
  private:
    shared_ptr< FooImpl<T> > m_impl;
};

当您声明Foo&lt;T&gt; 类型的变量时,它“修复”了T 的选择;

或使FooImpl 明确派生自一个公共基础:

class FooBase {
    // need to define the interface here
};

// this is a class definition whereas previously you only needed a declaration
template<typename T>
class FooImpl: public FooBase {
    // definition here
};

class Foo
{
  public:
    Foo getInstance(const string& fooType);

    // we needed the definition of FooImpl for this member
    // in addition this member is quite obviously a template
    template<typename T>
    void
    set(FooImpl<T> const& foo)
    {
        m_impl.reset(new FooImpl<T>(foo));
    }

    // not a member template!
    void
    use()
    {
        // any use of m_impl will be through the FooBase interface
    }

  private:
    shared_ptr<FooBase> m_impl;
};

对于给定的Foo 实例,可以动态设置任何类型的FooImpl&lt;T&gt;,然后通过FooBase 接口使用。这是一种type erasure,因为它在 C++ 世界中被称为。

【讨论】:

    【解决方案5】:

    我们可以使用模板来编写一个通用的智能指针类。以下 C++ 代码演示了相同的内容。我们不需要调用 delete 'ptr',当对象 'ptr' 超出范围时,它的析构函数会自动进行。

    #include<iostream>
    using namespace std;
    
    // A generic smart pointer class
    template <class T>
    class SmartPtr
    {
       T *ptr;  // Actual pointer
    public:
       // Constructor
       explicit SmartPtr(T *p = NULL) { ptr = p; }
    
       // Destructor
       ~SmartPtr() {
        cout <<"Destructor called" << endl;  
        delete(ptr);
       }
    
       // Overloading dereferncing operator
       T & operator * () {  return *ptr; }
    
       // Overloding arrow operator so that members of T can be accessed
       // like a pointer (useful if T represents a class or struct or 
       // union type)
       T * operator -> () { return ptr; }
    };
    
    int main()
    {
        SmartPtr<int> ptr(new int()); // Here we can create any data type pointer just like 'int'
        *ptr = 20;
        cout << *ptr;
        return 0;
    }
    

    输出:

    20

    调用析构函数

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-01-24
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-01-13
      相关资源
      最近更新 更多