3

I have the following container:

using KeyValue = mutable_pair<Key, Value>;
using MyContainer = boost::multi_index_container<
    KeyValue,
    boost::multi_index::indexed_by<
        boost::multi_index::hashed_unique<
            boost::multi_index::tag<KeyValueTag>,
            boost::multi_index::composite_key<
                KeyValue,
                boost::multi_index::const_mem_fun<KeyValue::first_type, unsigned int, &KeyValue::first_type::foo>,
                boost::multi_index::const_mem_fun<KeyValue::first_type, unsigned int, &KeyValue::first_type::bar>,
                boost::multi_index::const_mem_fun<KeyValue::first_type, unsigned int, &KeyValue::first_type::baz>,
            >
        >,
        boost::multi_index::hashed_non_unique<
            boost::multi_index::tag<BazTag>,
            boost::multi_index::const_mem_fun<KeyValue::first_type, unsigned int, &KeyValue::first_type::baz>
        >,
        boost::multi_index::hashed_non_unique<
            boost::multi_index::tag<BarTag>,
            boost::multi_index::const_mem_fun<KeyValue::first_type, unsigned int, &KeyValue::first_type::bar>
        >
    >
>;

Where mutable_pair is the boost provided example for maps, and Key is a class that contains const member accessors for foo, bar and baz.

The code compiles fine, however when trying to query by any index ie:

MyContainer c;
const auto& byBaz = c.get<BazTag>();
const auto it = byBaz.find(11);
// or
const auto [beg, end] = byBaz.equal_range(11);

it complains of

<long instantiation template error>
in mem_fun.hpp:58:23: error: no match for ‘operator*’ (operand type is ‘const mutable_pair<Key, Value>’)
   58 |     return operator()(*x);

What am I missing? I've been struggling with this for hours :(

Santiago
  • 379
  • 3
  • 14

1 Answers1

3

The code compiles fine

That's because templates members aren't instantiated unless you use them. You don't have valid indexes for your element type.

Your indexes are trying the equivalent of

KeyValue pair;
unsigned Key::(*pfoo)() = &Key::foo;

pair.*pfoo

Instead of

pair.first.*pfoo;

You need accessors for KeyValue, not Key

unsigned int getFoo(const KeyValue & pair) {
    return pair.first.foo();
}
unsigned int getBar(const KeyValue & pair) {
    return pair.first.bar();
}
unsigned int getBaz(const KeyValue & pair) {
    return pair.first.baz();
}

using MyContainer = boost::multi_index_container<
    KeyValue,
    boost::multi_index::indexed_by<
        boost::multi_index::hashed_unique<
            boost::multi_index::tag<KeyValueTag>,
            boost::multi_index::composite_key<
                KeyValue,
                boost::multi_index::global_fun<KeyValue, unsigned int, &getFoo>,
                boost::multi_index::global_fun<KeyValue, unsigned int, &getBar>,
                boost::multi_index::global_fun<KeyValue, unsigned int, &getBaz>,
            >
        >,
        boost::multi_index::hashed_non_unique<
            boost::multi_index::tag<BazTag>,
            boost::multi_index::global_fun<KeyValue, unsigned int, &getBaz>
        >,
        boost::multi_index::hashed_non_unique<
            boost::multi_index::tag<BarTag>,
            boost::multi_index::global_fun<KeyValue, unsigned int, &getBar>
        >
    >
>;

Aside: If you have C++17 and boost 1.69 or later, you can use a much terser syntax for keys:

boost::multi_index::key<&getFoo, &getBar, &getBaz>
boost::multi_index::key<&getBar>
Caleth
  • 52,200
  • 2
  • 44
  • 75
  • Excellent stuff. I'd personally add that using `mutable_pair` as the value type is probably a-typical and only used when people want to mimic std::map/std::unordered_map more closely. I consider that a strictly worse interface – sehe Oct 28 '22 at 10:54
  • @sehe it would be the same problem with `std::pair`. I could see having a `template using indexed_map = ...` that would use that – Caleth Oct 28 '22 at 11:02
  • 1
    I meant, using any pair at all is a anathema here. It's a mer side-note in case OP doesn't realize that. I did start with noting that excellence of the answer :) I just don't think the library shines when you try to emulate standard associative containers. Compare http://coliru.stacked-crooked.com/a/5f1057710b2cab22 – sehe Oct 28 '22 at 11:29
  • Amazing, this works! Just one thing @Caleth, it should be `KeyValue` not `KeyValue::first_type` on `global_fun` as the type – Santiago Oct 28 '22 at 12:50
  • @Santiago oops, yes. I'm now so used to the terse syntax that I forgot to change those – Caleth Oct 28 '22 at 12:52