1

I'm got a map of key type std::pair<Foo,Bar> and value in which I would like to insert into a map by passing the pair std::pair<std::pair<Foo,Bar> , int> to the insert function like so (online source)

struct Foo{};
struct Bar{};

typedef std::pair<Foo,Bar>        FooBar;
typedef std::pair<FooBar,int>     FooBarPair;
typedef std::map<FooBar,int>      FooBarMap;


struct FooBarData
{    
    operator const FooBarPair() const
    {
        return std::make_pair( std::make_pair( m_foo , m_bar ) , m_num );
    }

private:
    int     m_num;
    Foo     m_foo;
    Bar     m_bar;
};

int main()
{
    FooBarData  fbd;
    FooBarMap   fbm;

    fbm.insert( fbd );
}

clang error message

/usr/include/c++/v1/map:1041:9: note: candidate function not viable: no known conversion from 'FooBarData' to 'const value_type' (aka 'const pair<const key_type, mapped_type>') for 1st argument
    insert(const value_type& __v) {return __tree_.__insert_unique(__v);}
    ^
/usr/include/c++/v1/map:1050:14: note: candidate function template not viable: requires 2 arguments, but 1 was provided
    void insert(_InputIterator __f, _InputIterator __l)
         ^
/usr/include/c++/v1/map:1045:9: note: candidate function not viable: requires 2 arguments, but 1 was provided
    insert(const_iterator __p, const value_type& __v)

Is there any fix along these lines?

Olumide
  • 5,397
  • 10
  • 55
  • 104

2 Answers2

1

Add missing operator <.

Also, remove const from return type of conversion operator.

Edit After your change in Q:

You have to change your conversion to

operator std::pair<const FooBar, int>() const

Else, it would require 2 user conversions, which is not allowed.

Demo

Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • Thanks, but I can't use `emplace()` -- C++03. Also my map type is incorrect. I've just fixed it. – Olumide Mar 03 '16 at 15:43
  • 1
    You have to change your conversion to `operator std::pair() const` (Note the `const` removed and added). – Jarod42 Mar 03 '16 at 15:47
  • The global `operator<`'s did the trick. Out of interest, why does a non-const `FooBar` trigger two user-defined conversions? And why is that not allowed? – Olumide Mar 03 '16 at 15:58
  • 1
    Look at [multiple-implicit-conversions-on-custom-types-not-allowed](http://stackoverflow.com/questions/12847272/multiple-implicit-conversions-on-custom-types-not-allowed) – Jarod42 Mar 03 '16 at 16:04
1

(At least sometimes), the map implementations are/were red black trees. For map insert to work, your key type needs to support comparison (operator <). I cannot see this requirement to be met for your empty structs and the pair you create from them.

So as a first sanity check, I would replace Foo and Bar structs with

typedef int Foo;
typedef int Bar;

in order to see if something else is wrong with your code. If it still is not compiling, check if std::pair<> supports comparison.

Also, instead of typedef'ing the FooBarPair yourself, you can simply enjoy that the map template already did that for you. For example:

...
operator FooBarMap::value_type() { 
    ...
}

Here a fixed version, which compiles:

#include <map>
struct Foo { int x; };
struct Bar {};

typedef std::pair<Foo, Bar>        FooBar;
typedef std::map<FooBar, int>      FooBarMap;

bool operator <(const FooBarMap::key_type&lhs, const FooBarMap::key_type& rhs)
{
    return lhs.first.x < rhs.first.x;
}

struct FooBarData
{
    operator FooBarMap::value_type() const
    {
        return FooBarMap::value_type(FooBarMap::key_type(m_foo, m_bar), m_num);
    }

private:
    int     m_num;
    Foo     m_foo;
    Bar     m_bar;
};

int main()
{
    FooBarData  fbd;
    FooBarMap   fbm;

    fbm.insert(fbd);
}
BitTickler
  • 10,905
  • 5
  • 32
  • 53
  • Very sound advise Herr/Frau BitTickler. You seem like a very knowledgeable and experinced fellow ... or lady :) – Olumide Mar 03 '16 at 16:05