Search code examples
templatesc++11boosttagsbimap

Tagged boost::bimap in templates - do they work?


I am embedding a boost::bimap in a templated class, and after much trial-and-error I have discovered something that compiles and something that does not. I am using g++ (GCC) 4.9.2 20150212 (Red Hat 4.9.2-6) and Boost 1.55. I will give the full code in Example 1 and only the changed portions for Example 2 and Example 3. The function does not do anything useful, just testing the syntax and the compiler here.

Example 1 that does compile (complete listing):

#include <string>
#include <boost/bimap/bimap.hpp>
#include <boost/bimap/set_of.hpp>
#include <boost/bimap/unordered_set_of.hpp>
#include <boost/bimap/tags/tagged.hpp>

typedef double Address;

  struct Label1 {};
  struct Label2 {};

template< class TemplateParameter >
class Test
{
 private:

  typedef boost::bimaps::tagged< std::string, Label1 >  KeyType;
  typedef boost::bimaps::tagged< Address, Label2 > ValueType;

// No changes after this line in Example 2 or Example 3

  typedef boost::bimaps::unordered_set_of< KeyType > KeySet;
  typedef boost::bimaps::set_of< ValueType > ValueSet;

  typedef boost::bimaps::bimap < KeySet, ValueSet > BidirectionalMap;

  typedef typename BidirectionalMap::value_type Record;

  // Create the bimap

  BidirectionalMap TheBimap;

  void aFunction ( const Address & AnAddress, 
           const TemplateParameter & Parameters )
  {
    auto NewRecord = TheBimap.insert( Record( "TheGivenAdddress", AnAddress ) );
    auto ByAddress = TheBimap.by< Label1 >().find( "TheGivenAdddress" );
    auto ByNumber = TheBimap.by< Label2 >().find( AnAddress );
  }
};

I would like to encapsulate the labels inside the template class as there is no need for them to be known outside of the class. Ideally they should be private, but in order to avoid that access permissions create any problems, they are declared as public.

Example 2 that does not compile (except):

typedef double Address;

template< class TemplateParameter >
class Test
{
public:

  struct Label1 {};
  struct Label2 {};

private:

  typedef boost::bimaps::tagged< std::string, Label1 >  KeyType;
  typedef boost::bimaps::tagged< Address, Label2 > ValueType;

Both lines where the bimap is accessed by the label produces the following message:

error: expected primary-expression before ‘>’ token
 auto ByAddress = TheBimap.by< Label1 >().find( "TheGivenAdddress" );

This can be understood by Label1 needs full qualification, from being a part of the templated class, as follows.

auto ByAddress = TheBimap.by< Test<TemplateParameter>::Label1 >().find( "TheGivenAdddress" );

However the very same error is produced. Question 1: Does anyone understand why?

The real issue and reason for using the template class Test is that I want the type of Label 1 to be the template parameter. Thus, reverting to Example 1 and only replacing std::string with the template parameter.

Example 3 that does not compile (except):

typedef double Address;

  struct Label1 {};
  struct Label2 {};

template< class TemplateParameter >
class Test
{
private:

  typedef boost::bimaps::tagged< TemplateParameter, Label1 >  KeyType;
  typedef boost::bimaps::tagged< Address, Label2 > ValueType;

Again, both lines which access the map by the tags produces the above compilation error. Before rewriting the code to use "left" and "right" views, it would be good if anyone could help me understand Question 2: Why is it not possible to use a template parameter in the definition of the tagged type?

Thanks for all input!


Solution

  • The second case necessitates template qualification. The reason is that Label1 and Label2 are now dependent names.

    auto ByAddress = TheBimap.template by<Label1>().find("TheGivenAdddress");
    auto ByNumber  = TheBimap.template by<Label2>().find(AnAddress);
    

    See Where and why do I have to put the "template" and "typename" keywords?

    Question 2: Why is it not possible to use a template parameter in the definition of the tagged type?

    The same reason. You may also need typename qualification there.

    Demo

    Live On Coliru

    #include <string>
    #include <boost/bimap/bimap.hpp>
    #include <boost/bimap/set_of.hpp>
    #include <boost/bimap/unordered_set_of.hpp>
    #include <boost/bimap/tags/tagged.hpp>
    
    typedef double Address;
    
    namespace bm = boost::bimaps;
    
    template <class T>
    class Test {
      private:
        typedef bm::bimap<
              bm::unordered_set_of<bm::tagged<T,       struct key_idx> >,
              bm::set_of          <bm::tagged<Address, struct value_ix> >
          > BidirectionalMap;
    
        typedef typename BidirectionalMap::value_type Record;
    
        BidirectionalMap _theBimap;
    
      public:
        void aFunction(const Address &anAddress, T const& parameters)
        {
            auto newRecord = _theBimap.insert(Record("TheGivenAdddress", anAddress));
            auto byNumber  = _theBimap.template by<value_ix>().find(anAddress);
            auto byAddress = _theBimap.template by<key_idx>().find(parameters);
    
            (void) newRecord, (void) byAddress, (void) byNumber;
        }
    };
    
    int main() {
        Test<std::string> t;
        t.aFunction(3.14, "hello");
    }
    

    To really keep the tag types local to the class add

        struct key_idx;
        struct value_idx;
    

    (no need to define them). See it live