Brendan
Brendan

Reputation: 457

STL container holding class within declaration of that class

I have written a program in C++ that compiles with clang++ but not with g++ and I would like to understand why.

The essential part is this:

class Table {
    private:
        ...
        std::unordered_map<std::string,Table> table_map;
        ...
};

I thought that std::unordered_map would be using pointers "under the hood", so that this is legal. I'm not declaring a Table inside of a Table, but I'm creating a container that has pointer(s) to Table(s) inside of a Table. Or so I thought. Clang++ has no problem with this and the program runs fine.

However, g++ complains about this, saying "note: forward declaration of 'class Table'". This suggests I should explicitly make the std::unordered_map take a pointer (std::unordered_map<std::string,Table*>, or perhaps some flavor of smart pointer). That requires I do some extra work (and I'm dealing with code I wrote over a year ago, so I'm not eager to have to figure out what I did in order to make the necessary changes).

Is this a bug in g++ or is clang++ doing something clever that's not really part of the standard? Is there a better way to rewrite this code so that it compiles on both clang++ and g++?

I have another class called Value, and there's a std::unordered_map of Values within Table, so would I get around this problem by creating a superclass Element from which Value and Table both inherit, and then having Table contain a std::unordered_map<std::string,Element>? Or would such a map be unable to store Values and Tables because it was declared for Elements?

Upvotes: 2

Views: 135

Answers (2)

M.M
M.M

Reputation: 141618

This is undefined behaviour. Standard library containers may not be instantiated with incomplete types, unless it explicitly says that the container supports it (e.g. unique_ptr).

Your options are:

  • Redesign your code to not do this
  • Use a container library that does support this

Upvotes: 2

4pie0
4pie0

Reputation: 29724

The error is caused by std::unordered_map having to rehash the table and then copying of elements. The value stored in container must therefore be a complete type.

You can get over it by wrapping value into smart pointers:

#include <iostream>
#include <string>
#include <unordered_map>
#include <memory>           // shared_ptr

class Table {
    private:
    std::unordered_map<std::string, std::shared_ptr<Table> > t;
};

int main() {
    Table t;
    return 0;
}

Compiles.

Upvotes: 4

Related Questions