meabefir
meabefir

Reputation: 69

Is there any way to do forward declaration when only working with one file

I have 2 classes that depend on each other and I can't find a way to get forward declaration to work. I know how to do it using multiple cpp and header files, I was just curious if there's a way to do it in a single file.

class cls2;

class cls1
{
public:
    int vi;
    cls1(int v = 30)
    {
        vi = v;
    }
    cls1(cls2 p)
    {
        vi = p.vi;
    }
};

class cls2
{
public:

    int vi;
    cls2(int v = 20)
    {
        vi = v;
    }
    cls2(cls1 p)
    {
        vi = p.vi;
    }
};

The error I get is:

error: 'p' has incomplete type

Upvotes: 1

Views: 72

Answers (3)

Vlad from Moscow
Vlad from Moscow

Reputation: 310980

This error message

error: 'p' has incomplete type

means that in the point of the constructor definition

cls1(cls2 p)
{
    vi = p.vi;
}

the compiler does not know yet how the class cls2 is defined and whether it has the data member vi.

You can define constructors only when the class used as a parameter is a complete type.

For example

class cls2;

class cls1
{
public:
    int vi;
    cls1(int v = 30)
    {
        vi = v;
    }
    cls1(cls2 p);
};

class cls2
{
public:

    int vi;
    cls2(int v = 20)
    {
        vi = v;
    }
    cls2(cls1 p)
    {
        vi = p.vi;
    }
};

cls1::cls1(cls2 p)
{
    vi = p.vi;
}

Or instead of this forward declaration

class cls2;

you may use an elaborated type specifier in the constructor declaration of the class cls1

For example

class cls1
{
public:
    int vi;
    cls1(int v = 30)
    {
        vi = v;
    }
    cls1( class cls2 p);
};

class cls2
{
public:

    int vi;
    cls2(int v = 20)
    {
        vi = v;
    }
    cls2(cls1 p)
    {
        vi = p.vi;
    }
};

cls1::cls1(cls2 p)
{
    vi = p.vi;
}

It is interesting to note that using the constructors of the classes you can get an unexpected result.

For example for this declaration

cls1 c{ { 10 } };

there will be called this constrictor

cls1(int v = 30)
{
    vi = v;
}

But if you add one more pair of braces like this

cls1 c{ { { 10 } } };

then there will be called this constructor

cls1::cls1(cls2 p)
{
    vi = p.vi;
}

Here is a demonstrative program.

#include <iostream>

class cls1
{
public:
    int vi;
    cls1(int v = 30)
    {
        std::cout << "cls1( int )\n";
        vi = v;
    }
    cls1( class cls2 p);
};

class cls2
{
public:

    int vi;
    cls2(int v = 20)
    {
        vi = v;
    }
    cls2(cls1 p)
    {
        vi = p.vi;
    }
};

cls1::cls1(cls2 p)
{
    std::cout << "cls1( cls2 )\n";
    vi = p.vi;
}

int main()
{
    cls1 c1 { { 10 } };
    cls1 c2 { { { 10 } } }; 

    // or
    // cls1 c1 { { } };
    // cls1 c2 { { { } } }; 
}

The program output is

cls1( int )
cls1( cls2 )

Upvotes: 1

3CxEZiVlQ
3CxEZiVlQ

Reputation: 38499

Use references, that do not produce copies, thus no complete class declarations are required. And split method definitions, move them to the point where class declarations are complete.

class cls2;

class cls1
{
public:
    int vi;
    cls1(int v = 30)
    {
        vi = v;
    }
    cls1(const cls2& p);
};

class cls2
{
public:

    int vi;
    cls2(int v = 20)
    {
        vi = v;
    }
    cls2(const cls1& p)
    {
        vi = p.vi;
    }
};

cls1::cls1(const cls2& p)
{
    vi = p.vi;
}

Upvotes: 3

jtbandes
jtbandes

Reputation: 118681

The error is happening because the constructor needs to know how to copy the cls2 object. You can solve this by moving the cls1 constructor implementation after the cls2 declaration:

class cls2;

class cls1
{
public:
    int vi;
    cls1(int v = 30)
    {
        vi = v;
    }
    cls1(cls2 p);
};

class cls2
{
public:

    int vi;
    cls2(int v = 20)
    {
        vi = v;
    }
    cls2(cls1 p)
    {
        vi = p.vi;
    }
};


cls1::cls1(cls2 p) {
    vi = p.vi;
}

Upvotes: 0

Related Questions