Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to avoid "'identifier' uses undefined class/struct/union 'name'" error when forward declaration is not enough?

Tags:

c++

According to http://msdn.microsoft.com/en-us/library/9ekhdcxs(v=vs.80).aspx,

C2079 can also occur if you attempt to declare an object on the stack of a type whose forward declaration is only in scope.

class A;

class B {
    A a;   // C2079
};

class A {};

Possible resolution:

class A;

class C {};

class B {
   A * a;
   C c;
};
class A {};

My question is how do I eliminate this error when I have the following situation:

class A;  // Object

class B   // Container
{
   public:
     typedef int SomeTypedef;
   private:
     A a;   // C2079
};

class A {
   void Foo(B::SomeTypedef);
};

I can't declare A before declaring B because A needs to use B's typedef, and I can't declare B before A because of this error.

One possible solution is to use a pointer to A instead of a stack variable, but I don't want a pointer (in this case).

Another solution is to not use typedef, or not to put it inside class B. But what if it belongs in B and I want not to pollute my project's namespace, as in B::SomeTypedef is a more appropriate name than SomeTypedef?

like image 358
Dmitri Shuralyov Avatar asked Apr 27 '12 17:04

Dmitri Shuralyov


3 Answers

Your design is questionable, although perhaps nested classes is what you intend:

class B {
   public:
     typedef int SomeTypedef;
   private:
     class A {
       void Foo(SomeTypedef);
     };
     A a;
};

If not, this can also be solved with another class which is common in CRTP code.

template<typename T>
struct foo;

class A;
class B;

template<>
struct foo<B> {
  typedef int SomeTypedef;
};

class A {
   void Foo(foo<B>::SomeTypedef);
};

class B : foo<B> {
   private:
     A a;
};

Or you can use another namespace.

like image 148
Pubby Avatar answered Nov 12 '22 23:11

Pubby


Another method is use an intermediate class, plus pointers, its more long, but, it works:

This is header file, ( yes I know, "*.hpp" extension is not standard ):


ForwardClassExample.hpp

class ForwardClass {
public:
  virtual void DoSomething();
};

class ContainerClass {
   ForwardClass* Item;

   /* constructor */ ContainerClass();
   /* destructor */ ~ContainerClass();
};

class RealClass: ForwardClass {
  /* override */ virtual void DoSomething();
};

This is body file:


ForwardClassExample.cpp

/* constructor */ ContainerClass::ContainerClass()
{
  // create reference to forwaded class item
  this.Item = new RealClass();
}

/* destructor */ ContainerClass::~ContainerClass()
{
  // deletereference to forwaded class item
  free this.Item();
}

void ForwardClass::DoSomething()
{
  // ...
}

void RealClass::DoSomething()
{
  // ...
}

Note:

I suggest to get used to apply pointers to variables, instead of direct fields, its may looks more difficult, at start, but, eventually allows to do more stuff.

It also prepare you to use "references" in case, one day you have to work with other programming languages.

Cheers.

like image 3
umlcat Avatar answered Nov 12 '22 21:11

umlcat


Introduce the typedef where your design requires it, and then export it to wherever it makes the most sense for your user.

class A
{
public:
   typedef int SomeTypedef;
   void Foo(SomeTypedef);
};

class B
{
public:
   typedef A::SomeTypedef SomeTypedef;
private:
   A a;
};
like image 1
Ben Voigt Avatar answered Nov 12 '22 22:11

Ben Voigt