Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why can I pass a boost map_list_of to a function that takes a map, but not a constructor?

I'm trying to construct an object that takes a std::map as a parameter, by passing it the map contents using boost map_list_of.

This gives a compile error, however, when I try to do the same with a regular function that takes a std::map, it compiles fine!

#include <map>
#include <boost/assign.hpp>

struct Blah
{
    Blah(std::map<int, int> data) {}
};

void makeBlah(std::map<int, int> data) {}

int main()
{
    Blah b(boost::assign::map_list_of(1, 2)(3, 4));    // Doesn't compile.

    makeBlah(boost::assign::map_list_of(1, 2)(3, 4));  // Compiles fine!
}

The compile error I get is:

error: call of overloaded ‘Blah(boost::assign_detail::generic_list<std::pair<int, int> >&)’ is ambiguous
note: candidates are: Blah::Blah(std::map<int, int, std::less<int>, std::allocator<std::pair<const int, int> > >)
note:                 Blah::Blah(const Blah&)

What is the ambiguity, and how come it doesn't affect the regular functoin makeBlah, which as far as I can see, has the same signature as the Blah constructor?

And is there a better way of achieving this, short of making a makeBlah function that will constructor an object of Blah, as it looks like I will have to do?

(As an aside, I am doing this in a unit test using map_list_of to make the test input data creation more readable)

like image 460
nappyfalcon Avatar asked Oct 01 '15 17:10

nappyfalcon


2 Answers

map_list_of doesn't create a container, but it creates an

anonymous list which can be converted to any standard container

The conversion is done through a template user-defined conversion operator:

   template< class Container >
   operator Container() const; 

So in the context of the constructor, it cannot deduce whether to convert to the map<int, int> or to Blah. To avoid this, you can, for example, add a dummy constructor parameter:

class Blah
{
    public:
        Blah(std::map<int, int> data, int)
        {
        }

};

void makeBlah(std::map<int, int> data)
{
}

void myTest()
{
    Blah b(boost::assign::map_list_of(1, 2)(3, 4), 0);   

    makeBlah(boost::assign::map_list_of(1, 2)(3, 4));  
}

With the makeBlah you don't have such an ambiguity.

Alternatively, you could take the list type as the constructor parameter and then use it within Blah:

class Blah
{
    public:
        Blah(decltype(boost::assign::map_list_of(0, 0)) data)
           : m(data.to_container(m))
        {
        }

    std::map<int, int> m;
};
like image 199
Rostislav Avatar answered Nov 12 '22 09:11

Rostislav


since the advent of c++11, boost::assign has become less compelling than it was.

Blah b({{1, 2},{3, 4}});

should do it.

like image 2
Richard Hodges Avatar answered Nov 12 '22 07:11

Richard Hodges