2

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

jww
  • 97,681
  • 90
  • 411
  • 885

5 Answers5

3

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
{
    ...
};
Robᵩ
  • 163,533
  • 20
  • 239
  • 308
  • In the end, I could not use a smart pointer with a template parameter, and I had to switch to 3 classes (damn near exactly as Number 2 above). – jww Sep 05 '11 at 03:32
2

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; 
}; 
Paul Michalik
  • 4,331
  • 16
  • 18
  • Conceptually, this is similar to what I wanted. However, I did not want users to have to declare `Foo f`. I had to remove the parameterized smart pointer, and use 3 classes. – jww Sep 05 '11 at 03:33
  • Hm, with this arrangement of types you do not have to define `Foo`. You actually can't, because `Foo` is not a class template... Your users write expressions like `Foo tTmp = 5;` or write functions such as `void TakeFoo(Foo const & pF)` or use expressions like `TakeFoo(123.5);` at the call site. Whether this makes sense depends on what you want to achieve. – Paul Michalik Sep 05 '11 at 17:25
1

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.

Seth Carnegie
  • 73,875
  • 22
  • 181
  • 249
  • OK. let me get back to it (I've been working on GCC with a forward declaration of class FooImpl). – jww Sep 03 '11 at 02:43
  • I don't know in advance I am going to have a Blah, only a Blah. – jww Sep 03 '11 at 03:13
  • @noloader yeah I know, just make `Foo` a template. – Seth Carnegie Sep 03 '11 at 03:38
  • I would like to, but that borks its use in client code. I want users to be able to do `Foo f = Foo::getInstance(...)`. – jww Sep 03 '11 at 03:47
  • 1
    @noloader if `Foo` is a template, then `Foo f = ...` is not valid. It *must* be `Foo f = ...` for some `T`. – Luc Danton Sep 03 '11 at 03:49
  • @noloader you can always provide `typedef`s of course, but you probably can't do that because you probably won't know all the types the user will use it with aforetime. – Seth Carnegie Sep 03 '11 at 03:57
  • @Luc: I don't disagree with you. I just don't want users to have to do that nasty sort of thing. – jww Sep 05 '11 at 03:36
1

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.

Community
  • 1
  • 1
Luc Danton
  • 34,649
  • 6
  • 70
  • 114
0

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