Search code examples
c++boostboost-multi-index

C++ incomplete type used in nested name specifier when using boost::multi_index


I have the following files:

"declaration.hpp"

namespace ns {
    
class Derived;
using DerivedPtr = std::unique_ptr<Derived>;
 
}

"Base.hpp"

namespace ns {

class User;

class Base
{
public:
    uint64_t foo() const;
};

}

"Derived.hpp"

namespace ns {

class Derived : public Base
{
public:
    uint64_t bar() const;
};

}

"User.hpp"

#include "declaration.hpp"
namespace ns {
class User
{
private:
    std::map<uint64_t, DerivedPtr>  map;
};

}

I tried to change the map in User to be a multi index map, i.e in "declaration.hpp" I put

struct Foo{};
struct Bar{};
  
using Map = boost::multi_index_container<
        DerivedPtr,
        boost::multi_index::indexed_by<
            boost::multi_index::ordered_unique<boost::multi_index::tag<Foo>,
                boost::multi_index::const_mem_fun<Base, uint64_t, &Base::foo> >,
            boost::multi_index::ordered_non_unique<boost::multi_index::tag<Bar>,
                boost::multi_index::const_mem_fun<Derived, uint64_t, &Derived::bar> > > >;

and

#include "declaration.hpp"
namespace ns {
class User
{
private:
    Map  map;
};

}

but I get error: incomplete type 'ns::Derived' named in nested name specifier . I can't put #include "Derived.hpp" & #include "Base.hpp" in "declaration.hpp" because then I get error: member access into incomplete type due to the forward declaration in "Base.hpp".

Is there a way to achieve what I'm trying to do? Is it possible to use fixed values for indices, thus eliminating the need for the member functions?


Solution

  • Derived.hpp wasn't included and you need a set of boost headers.

    This works:

    Live On Compiler Explorer

    • File ./Base.cpp

       #include "Base.hpp"
      
       namespace ns {
           uint64_t Base::foo() const { return 9; }
       } // namespace ns
      
    • File ./Base.hpp

       #pragma once
       #include <cstdint>
      
       namespace ns {
           class User;
      
           class Base {
             public:
               uint64_t foo() const;
           };
      
       } // namespace ns
      
    • File ./Derived.cpp

       #include "Derived.hpp"
      
       namespace ns {
           uint64_t Derived::bar() const { return 42; }
       } // namespace ns
      
    • File ./Derived.hpp

       #pragma once
       #include "Base.hpp"
       namespace ns {
      
           class Derived : public Base {
             public:
               uint64_t bar() const;
           };
      
       } // namespace ns
      
    • File ./User.cpp

       #include "User.hpp"
      
       namespace ns {
           void User::do_something() {
               map.emplace(std::make_unique<Derived>());
               map.emplace(std::make_unique<Derived>());
           }
       } // namespace ns
      
    • File ./User.hpp

       #include "Derived.hpp"
       #include "declaration.hpp"
       #include <boost/multi_index/mem_fun.hpp>
       #include <boost/multi_index/ordered_index.hpp>
       #include <boost/multi_index_container.hpp>
       #include <boost/multi_index_container.hpp>
       // #include <map>
      
       namespace ns {
           namespace bmi = boost::multi_index;
           using Map     = boost::multi_index_container<
               DerivedPtr,
               bmi::indexed_by<bmi::ordered_unique<bmi::tag<struct Foo>,                                     //
                                                   bmi::const_mem_fun<Base, uint64_t, &Base::foo>>,          //
                               bmi::ordered_non_unique<bmi::tag<struct Bar>,                                 //
                                                       bmi::const_mem_fun<Derived, uint64_t, &Derived::bar>> //
                               >>;
      
           class User {
             public:
               void do_something();
      
             private:
               // std::map<uint64_t, DerivedPtr> map;
               Map map;
           };
      
       } // namespace ns
      
    • File main.cpp

       #include "User.hpp"
      
       int main() {
           ns::User user;
      
           user.do_something();
       };
      

    BONUS: C++17 key extraction

    Live On Compiler Explorer

    using Map     = boost::multi_index_container<
        DerivedPtr,
        bmi::indexed_by<bmi::ordered_unique<bmi::tag<struct Foo>, bmi::key<&Base::foo>>,
                        bmi::ordered_non_unique<bmi::tag<struct Bar>, bmi::key<&Derived::bar>>>>;