views:

65

answers:

1

I'm currently doing a lot of things on exception safety. (Herb Sutter's Exceptional C++, C++ FAQ Lite, etc)

In particular, I wanted to write and understand the reference counting example of C++ FAQ Lite, but I'm currently stuck on this part of the code:

class Smart_Ptr;

class Foo
{
private:
    friend class Smart_Ptr;
    unsigned int count_;

public:
    static Smart_Ptr create() // Error here.
    {
        return new Foo();
    }

private:
    Foo()
        : count_(0)
    { }

/* ... */
/* Smart_Ptr class initialized down here */

As you can see, I'm trying to use the Named Constructor Idiom to force the user not to create local variables of my Foo object. Essentially, this is exactly what is written in the C++ FAQ; I have merely changed Fred to Foo and FredPtr to Smart_Ptr, which I realize was a mistake because it's harder to see the connection now.

My compiler spits out the error:

error C2027: use of undefined type 'Smart_Ptr'

I'm not exactly sure why this is. Smart_Ptr is fully defined and a complete duplicate of the FAQ code. I have also copied the code in full, as is, and have received the same error.

End Crucial Part of Question

Since I keep mistakingly thinking I post 'enough' of the source code to debug the problem, and I keep ending up in the wrong, I will post the rest of the code here.

/* Foo.h */
class Smart_Ptr;

class Foo
{
private:
    friend class Smart_Ptr;
    unsigned int count_;

public:
    static Smart_Ptr create()
    {
        return new Foo();
    }

private:
    Foo()
        : count_(0)
    { }
};

class Smart_Ptr
{
private:
    Foo *p_; // p_ is NEVER null

public:
    Foo *operator-> () { return p_; }
    Foo& operator*  () { return *p_; }

    Smart_Ptr(Foo *p) 
        : p_(p)
    {
        ++p_->count_;
    }

    ~Smart_Ptr()
    {
        if (--p_->count_ == 0)
            delete p_;
    }

    Smart_Ptr(Smart_Ptr const& p)
        : p_(p.p_)
    {
        ++p_->count_;
    }

    Smart_Ptr& operator= (Smart_Ptr const& p)
    {
        Foo *const old = p_;
        p_ = p.p_;
        ++p_->count_;

        if (--old->count_ == 0)
            delete old;

        return *this;
    }
};
+3  A: 

You can't write a function that returns Smart_Ptr by value, until Smart_Ptr is defined. A forward declaration isn't enough.

The code you link to contains the comment, // Defined below class FredPtr {...};, but you have defined the create function in the Foo class definition. If you look closely at the code after "the changes to class Fred would be:", you'll see that create is only declared in the class: it's defined later, by the following code:

inline FredPtr Fred::create()             { return new Fred(); }

All you need is to do the same.

Steve Jessop
Smart_Ptr is defined? Look at the rest of my code. Smart_Ptr is defined directly below the Foo class. The same is true of the canonical Fred and FredPtr class of the FAQ.
SoulBeaver
Saying "it's defined directly below" is rather like saying "the stable door is shut, I shut it right after the horse bolted". It's not (yet) defined at the point it's needed, which is all that matters.
Steve Jessop
Okay, I understand that. I switched the arrangement, and, lo-and-behold, I get the same error, but now for the Fred object. So I define the create function directly below, and it works. This is odd, why is that? Is there any other way to solve the problem other than defining the function directly below all the necessary classes?
SoulBeaver
No, there's no other solution. The C++ compiler reads your code in order, that's just the way the language is specified, and it keeps things simpler (not that parsing and compiling C++ is simple). You could consider templates an exception to the rule in some respects - template parameters and types which are dependent on them do not need to be defined at the point where the template is defined, only when it's instantiated.
Steve Jessop
@Steve: You said "You can't write a function that returns Smart_Ptr by value". But I am unable to return even a pointer, not just by value for an undefined class.
bjskishore123
@bjskishore: You can return a pointer to a forward-declared class, but if you want it to point to an actual object (not null), you'll need an object from somewhere, and you can't allocate one with `new` unless you have the definition.
Steve Jessop