简体   繁体   中英

Smart Pointer for a template class?

I suspect I can't do this directly using a PIMPL pattern. Is it possible to have a smart pointer to a template class? I have not been able to compile by turning knobs on the shared_ptr declaration.

// ============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
{
    ...
};

Under Visual Studio 2008: "error C2065: 'T' : undeclared identifier". I receive a similar error under GCC. If I un-parameterize FooImpl (so that FooTempl inherits from FooImpl), the code will compile.

I suspect I can't paramaterize the smart pointer, but I could be wrong.

EDIT: The second Visual Studio error is more telling: "error C3203: 'FooImpl' : unspecialized class template can't be used as a template argument for template parameter 'T', expected a real type"

Jeff

I'm not entirely certain what you are trying to accomplish, but does this help?

Try 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
{
    ...
};

Try 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
{
    ...
};

You're doing it right, just make sure T is defined. This compiles for me on 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>()) { }
};

If you're on an older compiler that hasn't incorporated this feature of C++11 yet, change

shared_ptr<Blah<int>> ptr;

To

shared_ptr<Blah<int> > ptr;

So the compiler doesn't think the >> is a right shift. C++11 doesn't have this problem though.

I don't know in advance I am going to have a Blah, only a Blah.

From the language point of view, Blah<T> is meaningless because T doesn't exist. Depending on what you're exactly trying to do, you can

make Foo a template, too, so that you can declare a template parameter T :

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

which 'fixes' the choice of T when you declare a variable of type Foo<T> ;

or make FooImpl explicitly derive from a common base:

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

where for a given Foo instance any kind of FooImpl<T> can be set dynamically and then used through the FooBase interface. This is a kind of type erasure as it's called in the C++ world.

The code you have posted cannot compile since T does not mean anything in the context of Foo . The compiler expects a type called T here which does not exist there... Not entirely sure what you are trying to accomplish, but wouldn't the following solve your problem?

// ============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; 
}; 

We can use templates to write a generic smart pointer class. Following C++ code demonstrates the same. We don't need to call delete 'ptr', when the object 'ptr' goes out of scope, destructor for it is automatically.

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

out put:

20

Destructor called

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM