0
votes

Why does including a declaration resolve a missing definition error? This is related to an earlier question.

In the following set of files I am surprised that a definition is sought for a class that is not used and then when I include only a declaration for the class the program compiles.

I have added comments for the includes that I had to add, but can't understand why they're needed in those particular .cpp files.

A.h

#ifndef A_h
#define A_h

#include <map>

class C;

class A {
public:
    C& add();
    std::map<int,C> Cmap;
    void dosomethingwithC();
};

#endif

B.h

#ifndef B_h
#define B_h

#include <map>

class A;

class B {
public:
    A& add();
    std::map<int,A> Amap;
};

#endif

C.h

#ifndef C_h
#define C_h

#include <map>

class B;

class C {
public:
    B& add();
    std::map<int,B> Bmap;
};

#endif

A.cpp

#include "A.h"

#include "B.h"// Required here, but B isn't used in this file.
              // Without B.h the error is
              // error C2079: 'std::pair<const _Kty,_Ty>::second' uses undefined class 'B'
              // But B.h only includes the declaration, not the definition

#include "C.h"

C& A::add()
{
    auto emplace_results = Cmap.emplace(std::piecewise_construct,
        std::forward_as_tuple(3),
        std::forward_as_tuple());
    auto pair_iterator = emplace_results.first;
    auto& emplaced_pair = *pair_iterator;
    auto& map_value = emplaced_pair.second;
    return map_value;
}


void A::dosomethingwithC()
{
    Cmap[3].add();
}

B.cpp

#include "A.h"
#include "B.h"
#include "C.h" // also required here

A& B::add()
{
    auto emplace_results = Amap.emplace(std::piecewise_construct,
        std::forward_as_tuple(3),
        std::forward_as_tuple());
    auto pair_iterator = emplace_results.first;
    auto& emplaced_pair = *pair_iterator;
    auto& map_value = emplaced_pair.second;
    return map_value;;
}

C.cpp

#include "A.h" // also required here
#include "B.h"
#include "C.h"

B& C::add()
{
    auto emplace_results = Bmap.emplace(std::piecewise_construct,
        std::forward_as_tuple(3),
        std::forward_as_tuple());
    auto pair_iterator = emplace_results.first;
    //auto& emplaced_pair = *pair_iterator;
    std::pair<const int,B>& emplaced_pair = *pair_iterator;
    auto& map_value = emplaced_pair.second;
    return map_value;
}

Main.cpp

#include "A.h"
#include "B.h"
#include "C.h"

int main()
{
    C c;
    auto& emplacedB = c.add();
    auto& emplacedA = emplacedB.add();
    auto& emplacedC = emplacedA.add();
    emplacedC.add();
    return 0;
}
2

2 Answers

2
votes
  1. B is used in C.cpp. It is used in the definition of the member variable of C.

  2. B.hpp doesn't contains a declaration of class B (which would be class B;). It contains the definition of it (but not of its members).

0
votes

Incomplete type (forward declaration) is not allowed in STL container, even though boost containers allow it. I think it would fix this issue if you include required header files instead of forward declarations.