-1

I have the below class (which is basically a wrapper for std::vector), and one of the functions returns a variable of the type AInteger (which is basically a wrapper for int). Now the type AInteger is used multiple times throughout the class, yet the compiler starts complaining at a very specific position. When I remove the "getSize()" function, everything compiles just fine.

There is mutual inclusion, so I need the forward declaration for everything to work.

One of the problems is that the AList class is a template, so it is impossible to move the definitions to a .cpp file (which would normally solve the problem).

What am I doing wrong?

Here's the class:

#ifndef ALIST_H
#define ALIST_H

#include <vector>

#include "AInteger.h"

class AInteger;

template<typename VALUE>
class AList {
public:
    AList() {
    }

    AList(const std::vector<VALUE> list) {
        value = list;
    }

    ~AList() {
    }

    operator const std::vector<VALUE>() const {
        return value;
    }

    std::vector<VALUE> toStdVector() const {
        return value;
    }

    VALUE operator [](const AInteger index) const {
        return value.at(index);
    }

    void add(const VALUE value) {
        this->value.push_back(value);
    }

    VALUE get(const AInteger index) const {
        return value[index];
    }

    AInteger getSize() const { // ERROR OCCURS HERE
        return value.size();
    }

    void remove(const AInteger index) {
        value.erase(index);
    }

private:
    std::vector<VALUE> value;
};

#endif

Output:

1>------ Build started: Project: ALibrary, Configuration: Debug Win32 ------
1>  ASocket.cpp
1>d:\programs\programming\visual studio projects\c++\alibrary\alibrary\alist.h(43): error C2027: use of undefined type 'AInteger'
1>  d:\programs\programming\visual studio projects\c++\alibrary\alibrary\alist.h(8): note: see declaration of 'AInteger'
1>  d:\programs\programming\visual studio projects\c++\alibrary\alibrary\alist.h(53): note: see reference to class template instantiation 'AList<VALUE>' being compiled
1>  AInteger.cpp
1>d:\programs\programming\visual studio projects\c++\alibrary\alibrary\alist.h(43): error C2027: use of undefined type 'AInteger'
1>  d:\programs\programming\visual studio projects\c++\alibrary\alibrary\alist.h(8): note: see declaration of 'AInteger'
1>  d:\programs\programming\visual studio projects\c++\alibrary\alibrary\alist.h(53): note: see reference to class template instantiation 'AList<VALUE>' being compiled
1>  AHttpRequest.cpp
1>d:\programs\programming\visual studio projects\c++\alibrary\alibrary\alist.h(43): error C2027: use of undefined type 'AInteger'
1>  d:\programs\programming\visual studio projects\c++\alibrary\alibrary\alist.h(8): note: see declaration of 'AInteger'
1>  d:\programs\programming\visual studio projects\c++\alibrary\alibrary\alist.h(53): note: see reference to class template instantiation 'AList<VALUE>' being compiled
1>  ABoolean.cpp
1>d:\programs\programming\visual studio projects\c++\alibrary\alibrary\alist.h(43): error C2027: use of undefined type 'AInteger'
1>  d:\programs\programming\visual studio projects\c++\alibrary\alibrary\alist.h(8): note: see declaration of 'AInteger'
1>  d:\programs\programming\visual studio projects\c++\alibrary\alibrary\alist.h(53): note: see reference to class template instantiation 'AList<VALUE>' being compiled
1>  Generating Code...
1>  Compiling...
1>  AString.cpp
1>  Generating Code...
2>------ Build started: Project: BitHoarder, Configuration: Debug Win32 ------
2>  SystemHandler.cpp
2>d:\programs\programming\visual studio projects\c++\alibrary\alibrary\alist.h(43): error C2027: use of undefined type 'AInteger'
2>  d:\programs\programming\visual studio projects\c++\alibrary\alibrary\alist.h(8): note: see declaration of 'AInteger'
2>  d:\programs\programming\visual studio projects\c++\alibrary\alibrary\alist.h(53): note: see reference to class template instantiation 'AList<VALUE>' being compiled
2>  Main.cpp
2>d:\programs\programming\visual studio projects\c++\alibrary\alibrary\alist.h(43): error C2027: use of undefined type 'AInteger'
2>  d:\programs\programming\visual studio projects\c++\alibrary\alibrary\alist.h(8): note: see declaration of 'AInteger'
2>  d:\programs\programming\visual studio projects\c++\alibrary\alibrary\alist.h(53): note: see reference to class template instantiation 'AList<VALUE>' being compiled
2>  Generating Code...
========== Build: 0 succeeded, 2 failed, 0 up-to-date, 0 skipped ==========

If any more code is needed just ask and I will provide.

Thanks!

EDIT: Here's the code that's inside "AInteger.h"

#ifndef AINTEGER_H
#define AINTEGER_H

#include "AList.h"

template<typename VALUE>
class AList;

class AInteger {
public:
    AInteger();
    AInteger(const int);
    ~AInteger();

    operator const int() const;

    int toInt() const;

    AInteger operator=(const AInteger &);
    AInteger & operator++();
    AInteger & operator--();

    AList<AInteger>splitByNumber(const AInteger &);

private:
    int value;
};

#endif

To make matters even more confusing, the following class, which does THE EXACT SAME THING does not produce the error:

#ifndef ADICTIONARY_H
#define ADICTIONARY_H

#include <map>

#include "AInteger.h"

class AInteger;

template<typename KEY, typename VALUE, typename COMPARE = std::less<KEY>>
class ADictionary {
public:
    ADictionary() {
    }

    ADictionary(const std::map<KEY, VALUE, COMPARE> dictionary) {
        value = dictionary;
    }

    ~ADictionary() {
    }

    operator const std::map<KEY, VALUE, COMPARE>() const {
        return value;
    }

    std::map<KEY, VALUE, COMPARE> toStdMap() const {
        return value;
    }

    VALUE operator [](const KEY key) const {
        return value.at(key);
    }

    void add(const KEY key, const VALUE value) {
        this->value.insert(std::make_pair(key, value));
    }

    VALUE get(const KEY key) const {
        return value[key];
    }

    AInteger getSize() const { // No error here
        return value.size();
    }

    void remove(const KEY key) {
        value.erase(value.find(key));
    }

private:
    std::map<KEY, VALUE, COMPARE> value;
};

#endif
Qub1
  • 1,154
  • 2
  • 14
  • 31
  • 2
    Isn't AInteger declared in "AInteger.h"? If so, why do you need the declaration after including? – vmg Oct 06 '15 at 21:15
  • @vmg There's mutual inclusion, so I need to forward declare or else I'll get errrors. – Qub1 Oct 06 '15 at 21:16
  • 1
    Forward declaring does not magically remove the mutual inclusion. – juanchopanza Oct 06 '15 at 21:18
  • @juanchopanza No, but the header guards do right? – Qub1 Oct 06 '15 at 21:19
  • 1
    Nope. They don't. There are quite a few duplicates about this. Just look around. For example http://stackoverflow.com/questions/625799/resolve-circular-dependencies-in-c – juanchopanza Oct 06 '15 at 21:19
  • The usual way of fixing a mutual dependence is to move the code out of the header and into a source file. That doesn't work for template classes though, so this is a lot harder than it should be. – Mark Ransom Oct 06 '15 at 21:22
  • @MarkRansom Yes you're right, the template is what's causing all the issues. Unfortunately I can't get around that since the template is a fundamental part of the class. Is there anything I can do to fix that? – Qub1 Oct 06 '15 at 21:25
  • @Qub1 you've only given us half the problem, no solutions pop to mind yet. And I'd mention the part about the templates, or this risks being closed as a duplicate. – Mark Ransom Oct 06 '15 at 21:26
  • @MarkRansom I've included the "AInteger.h" class, I hope that's enough information. As I've stated in the original question, I will provide more if necessary. – Qub1 Oct 06 '15 at 21:30
  • @Qub1: That's strange. That header uses a template called `List`, but the one you've shown us is called `AList`... – Christian Hackl Oct 06 '15 at 21:30
  • @ChristianHackl Whoops, that's a typo :S (I edited the class a little to make the error more obvious) – Qub1 Oct 06 '15 at 21:32
  • @juanchopanza I'm pretty new to C++ so forgive me for asking, but what does that mean? I have lots of other classes which do the same thing and they all work. – Qub1 Oct 06 '15 at 21:33
  • @juanchopanza `AInteger` isn't a class? Okay now I'm really confused, I've clearly declared it as a class in the header file. – Qub1 Oct 06 '15 at 21:38
  • 1
    @Qub1 Sorry, I misread the header. Please ignore my last two comments. You can probably fix the circular include by removing `#include "AList.h"` from `AInteger.h`. – juanchopanza Oct 06 '15 at 21:39
  • `AInteger.h` includes `AList.h`. `AList.h` includes `AInteger.h`. So what is wrong? Everything. And since both classes require to use each other by value, the design is simply invalid. @juanchopanza No, he cannot. `AInteger::splitByNumber` returns `AList` by value, so `AList.h` is required here. – Mateusz Grzejek Oct 06 '15 at 21:43
  • @MateuszGrzejek Okay so I've removed the functions in AInteger that depend on AList and I've also removed the #include from AInteger. Still the same errors. – Qub1 Oct 06 '15 at 21:44
  • @MateuszGrzejek And to make matters more confusing, there is another class which basically does the same thing but does not produce errors. I've included it in the question. – Qub1 Oct 06 '15 at 21:45
  • You do not provide informations required to form a valid answer. From build log it is easy to say, that this may be a very complex (and messy) project with lots of includes. Problem may have originated in totally different location. This question seems unsalvageable to me, sorry. – Mateusz Grzejek Oct 06 '15 at 21:49
  • the big difference is AInteger.h does not include ADictionary.h, but does include AList.h. [You have a circular reference, my friend.](http://stackoverflow.com/questions/32128122/trying-to-use-a-class-in-one-header-file-in-another-header-file/32129089#32129089) – user4581301 Oct 06 '15 at 21:50
  • @user4581301 As I've stated above, even when removing that reference it still does not work. – Qub1 Oct 06 '15 at 21:51
  • @MateuszGrzejek Messy? I think my code is pretty clean. And the project is only 5 classes in size. – Qub1 Oct 06 '15 at 21:52
  • Ah, of course it is! But first of all, I meant logical design. And secondly, your problems do not seem to confirm your statement. But we are going OT now. I would help if I could, but the question is not answerable at the moment. – Mateusz Grzejek Oct 06 '15 at 21:56
  • @MateuszGrzejek I think the problem has something to do with the lib file not being generated (I'm using these classes as a static library for another project). This would explain the weird issues and why there doesn't seem to be a solution. I'm going to try to poke around a little more to see if I can fix it. – Qub1 Oct 06 '15 at 22:00
  • @Qub1 of course it doesn't, but it will fail differently. You have defined a hierarchy where both classes require full knowledge of the other. You cannot build with a forward definition because the forward definition does not provide complete information and you can't fully define both classes before one needs to know the other. This cannot be done. You need to redesign. – user4581301 Oct 06 '15 at 22:03
  • @user4581301 I believe I do, yes, but how would I be able to have both classes require each other without causing such errors? – Qub1 Oct 06 '15 at 22:12
  • One of them needs to operate with minimal knowledge of the other. That means using nothing but references and pointers in the function prototypes. The simple fix is working over `AListsplitByNumber(const AInteger &);` to `std::unique_ptr> splitByNumber(const AInteger &);` The AList is created with `new` inside the cpp file implementing AInteger where AList.h can be safely included. It is then wrapped in a [unique_ptr](http://en.cppreference.com/w/cpp/memory/unique_ptr) to ensure deletion when it goes out of scope – user4581301 Oct 06 '15 at 22:32
  • @user4581301 Strangely enough I've managed to fix it by simply replacing `AInteger getSize() const` with `AInteger & getSize() const`. Although I'm not quite sure why. – Qub1 Oct 06 '15 at 22:37
  • 1
    That returns a reference to an AInteger rather than an AInteger. Since the only AInteger you will have available inside `getSize` is a local variable doomed to go out of scope and become invalid when the function returns, the reference to that AInteger will be invalid by the time you can look at it. Sadly that will compile, but probably blow up when you run it. – user4581301 Oct 06 '15 at 22:44
  • @user4581301 strangely it compiles and runs just fine, nothing is going out of scope. I'm really confused. Maybe better to just let it be since it's working. Maybe this has something to do with template classes not being actual classes but templates for classes that are generated at compile time? – Qub1 Oct 06 '15 at 22:48
  • Trust me on this. Local variables go out of scope. Any use you get out of it before its storage is reassigned and reused is by the grace of GOD and the Giver Of Data is a capricious deity. One day it will work and another it won't. Do not count on it working. Do count on bizarro bugs when it doesn't. – user4581301 Oct 06 '15 at 23:12
  • @Qub1 I've tacked on an answer with a couple solutions to this problem that are safe to use. – user4581301 Oct 06 '15 at 23:18
  • @Qub1: Trust us... your solution does *not* work. You cannot return a reference like this. It is undefined behavior. Perhaps I will add more to my answer later today. – Christian Hackl Oct 07 '15 at 05:00

2 Answers2

4

The implementation of getSize() must create an instance of AInteger. This is only possible if the full definition of AInteger is known. You cannot create an instance of a type which is only forward-declared.

And that's exactly what the compiler tells you: error C2027: use of undefined type 'AInteger'. It does not ignore the forward declaration but tells you that it's not enough.

As for the #include "AInteger.h", you do not show its contents, but possible problems are:

  • Buggy include guard in the header file causes the compiler to skip the definition.
  • The header file defines a different type with a similar name.
  • The definition of AInteger is within a namespace.
Christian Hackl
  • 27,051
  • 3
  • 32
  • 62
2

What's gone wrong is answered very well in Christian Hackl's answer and normally I'd drop it there, but I can't explain how the OP can fix this properly in a comment.

First a revised AInteger.h

#ifndef AINTEGER_H
#define AINTEGER_H

template<typename VALUE>
class AList;

class AInteger {
public:
    AInteger();
    AInteger(const int);
    ~AInteger();

    //operator const int() const;

    int toInt() const;

    AInteger operator=(const AInteger &);
    AInteger & operator++();
    AInteger & operator--();

    std::unique_ptr<AList<AInteger>> splitByNumber(const AInteger &);
    void splitByNumber(const AInteger &,
                       AList<AInteger> &);

private:
    int value;
};

#endif

The include of AList.h is gone. The forward declaration of AList remains I've provided two different splitByNumber methods. Pick one. The first creates and returns a pointer protected by a smart pointer. The second approach, and my personal preference, takes a reference to an AList created by the caller.

The thing is neither know anything about the inner workings of AList because all they care is about is A) It exists and b) they can get the address of one.

Bear with me while I explore both because I think they are both educational.

Alist.h remains unchanged except for the removal of the forward declaration of AInteger.

The two splitByNumber candidates sit in AInteger's implementation file which can safely include both AInteger.h and AList.h and thus has complete knowledge of both at the same time and can do all the magical things AIntegers and ALists can do.

std::unique_ptr<AList<AInteger>> AInteger::splitByNumber(const AInteger & integer)
{
    std::unique_ptr<AList<AInteger>> listp(new AList<AInteger>());

    // do stuff with listp
    return listp;
}

void AInteger::splitByNumber(const AInteger & integer,
                             AList<AInteger> & list)
{
    // do stuff with list
}

Using the pointer version:

std::unique_ptr<AList<AInteger>> alistp = aitest1.splitByNumber(aitest2);
alistp->add(somenumber);

Using the reference version:

AList<AInteger> alist; // first create an empty AList
aitest1.splitByNumber(aitest2, alist); // pass it into the function
alist.add(somenumber); // use the AList
user4581301
  • 33,082
  • 7
  • 33
  • 54