kakyo
kakyo

Reputation: 11580

Compiler error C2664 using std::map and custom class as its value

I've done a lot of searching on this issue but none of the answers helped.

My setup

I have a large codebase, where in one class Bar


// Bar.h

class Bar {
public:
   Bar();
   ~Bar();

public:
    ... // a long list of other methods.

private:
    std::map<std::string, Foo> m_foos;

};

then in its implementation

// Bar.cpp

Bar::Bar() {
   m_foos.insert(std::make_pair(std::string("id"), Foo()));              // Error 1
   m_foos.insert({std::string("id"), Foo()});                            // Error 2
   m_foos.insert(std::pair<std::string, Foo>(std::string("id"), Foo())); // Error 3
}

Trying to compile the above code gives me:

Error 1

error C2664: 'std::_Tree_iterator<std::_Tree_val<std::_Tree_simple_types<_Ty>>> std::_Tree<std::_Tmap_traits<_Kty,Foo,_Pr,_Alloc,false>>::insert(std::_Tree_const_iterator<std::_Tree_val<std::_Tree_simple_types<_Ty>>>,const std::pair<const _Kty,Foo> &)': cannot convert argument 1 from 'std::pair<std::basic_string<char,std::char_traits<char>,std::allocator<char>>,Foo>' to 'std::pair<const _Kty,_Ty> &&'

Error 2

error C2664: 'std::_Tree_iterator<std::_Tree_val<std::_Tree_simple_types<_Ty>>> std::_Tree<std::_Tmap_traits<_Kty,Foo,_Pr,_Alloc,false>>::insert(std::_Tree_const_iterator<std::_Tree_val<std::_Tree_simple_types<_Ty>>>,const std::pair<const _Kty,Foo> &)': cannot convert argument 1 from 'initializer list' to 'std::pair<const _Kty,_Ty> &&'

Error 3

error C2440: '<function-style-cast>': cannot convert from 'initializer list' to 'std::pair<std::basic_string<char,std::char_traits<char>,std::allocator<char>>,Foo>'

However, with the same setup and compiler, the following much-simplified code compiles successfully:

#include "pch.h"
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <map>
#include <string>
#include <vector>

class Foo {
public:
    Foo() { mi = 1; }
    ~Foo() {}

private:
    int mi;
};

class MyFoo : public Foo {
public:
    MyFoo() :Foo() {
        mj = 2;
        mk = nullptr;
    }
    ~MyFoo() {}

private:
    int mj;
    int* mk;
};

class YourFoo : public Foo {
public:
    YourFoo() :Foo() { mj = 2; }
    ~YourFoo() {}

private:
    int mj;
};

int main()
{
    std::map<int, Foo> yourmap;
    yourmap.insert(std::make_pair(3, Foo()));
    yourmap.insert({ 4, Foo() });
    yourmap.insert(std::pair<int, Foo>(5, Foo()));

}

Where am I wrong?

UPDATE

So I can finally reproduce it. It's due to the fact that the offending class hierarchy removed their copy constructors and operator =.

The modified example can reproduce the errors.

#include "pch.h"
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <map>
#include <string>
#include <vector>

class Foo {
public:
    Foo() { mi = 1; }
    ~Foo() {}

    // NO COPIES.   
    Foo(const Foo &) = delete;
    Foo& operator = (const Foo &) = delete;

private:
    int mi;
};

class MyFoo : public Foo {
public:
    MyFoo() :Foo() {
        mj = 2;
        mk = nullptr;
    }
    ~MyFoo() {}

    // NO COPIES.
    MyFoo(const MyFoo &) = delete;
    MyFoo& operator = (const MyFoo &) = delete;

private:
    int mj;
    int* mk;
};

class YourFoo : public Foo {
public:
    YourFoo() :Foo() { mj = 2; }
    ~YourFoo() {}

    // NO COPIES.
    YourFoo(const YourFoo &) = delete;
    YourFoo& operator = (const YourFoo &) = delete;

private:
    int mj;
};


int main()
{
    std::map<int, Foo> yourmap;
    yourmap.insert(std::make_pair(3, Foo()));
    yourmap.insert({ 4, Foo() });
    yourmap.insert(std::pair<int, Foo>(5, Foo()));

}

So I guess the problem is that std::map must copy elements into its memory space. Then it just bumps into classes without copy constructors and complains. But the error messages are so misleading that I just can't figure out. The actual class hierarchy is pretty huge, so I didn't see those deleted parts. Guess someone does not want these things duplicated.

If anybody can instruct how to decipher the compiler error messages near the beginning of my question so that I may get a better idea where to look, it would be greatly appreciated!

Upvotes: 1

Views: 1990

Answers (3)

Scheff&#39;s Cat
Scheff&#39;s Cat

Reputation: 20141

Instances of classes without a usable copy constructor / copy assignment can still be used in std::map using a little special trick:

  • emplace instead of insert (to construct the map entry in place)
  • using std::piecewise_construct.

More about this on cppreference.com: std::map::emplace

Applied to OPs sample code:

#include <iostream>
#include <map>
#include <string>

class Foo {
public:
    Foo(): mi("1") { }
    ~Foo() = default;
    Foo(const Foo&) = delete;
    Foo& operator=(const Foo&) = delete;

private:
    std::string mi;
};

std::ostream& operator<<(std::ostream &out, const Foo &foo)
{
  return out << "Foo()";
}

int main()
{
  std::map<std::string, Foo> yourmap;
#if 0 // WON'T COMPILE due to deleted Foo::Foo(const Foo&):
  yourmap.insert(std::make_pair("3", Foo()));
  yourmap.insert({ "4", Foo() });
  yourmap.insert(std::pair<std::string, Foo>("5", Foo()));
#endif // 0
  yourmap.emplace(std::piecewise_construct,
    std::forward_as_tuple("3"), // std::string("3")
    std::forward_as_tuple()); // Foo()
  // check result
  for (auto &entry : yourmap) {
    std::cout << '"' << entry.first << "\": " << entry.second << '\n';
  }
}

Output:

"3": Foo()

Live Demo on coliru

Upvotes: 1

Megidd
Megidd

Reputation: 7938

I build this program on Visual Studio Community 2017 without any problem:

#include "pch.h"
#include <iostream>
#include <map>

class Foo {
public:
   Foo();
};

Foo::Foo() {
   std::cout << "Foo is being constructed\n";
}

class Bar {
public:
   Bar();
   ~Bar();

private:
   std::map<std::string, Foo> m_foos;

};

Bar::Bar() {
   m_foos.insert(std::make_pair(std::string("id"), Foo()));              
   m_foos.insert({ std::string("id"), Foo() });                            
   m_foos.insert(std::pair<std::string, Foo>(std::string("id"), Foo())); 
}

Bar::~Bar() {
   std::cout << "Destructing bar\n";
}

int main()
{
   Bar bar = Bar();
   std::cout << "Hello World!\n"; 
}

Program output on console:

C:\Users\me\source\repos\SO0\Debug>SO0.exe
Foo is being constructed
Foo is being constructed
Foo is being constructed
Hello World!
Destructing bar

Upvotes: 0

Mohammad reza Kashi
Mohammad reza Kashi

Reputation: 369

#include "pch.h"
#include <iostream>
#include <algorithm>
#include <cstdio>
#include <map>
#include <string>
#include <vector>

class Foo {
public:
    Foo() { mi = 1; }
    ~Foo() {}

    // NO COPIES.   
    Foo(const Foo &) = delete;
    Foo& operator = (const Foo &) = delete;

private:
    int mi;
};

class MyFoo : public Foo {
public:
    MyFoo() :Foo() {
        mj = 2;
        mk = nullptr;
    }
    ~MyFoo() {}

    // NO COPIES.
    MyFoo(const MyFoo &) = delete;
    MyFoo& operator = (const MyFoo &) = delete;

private:
    int mj;
    int* mk;
};

class YourFoo : public Foo {
public:
    YourFoo() :Foo() { mj = 2; }
    ~YourFoo() {}

    // NO COPIES.
    YourFoo(const YourFoo &) = delete;
    YourFoo& operator = (const YourFoo &) = delete;

private:
    int mj;
};


int main()
{
    std::map<int, std::shared_ptr<Foo> > objects;
    objects.insert(std::pair<int, std::shared_ptr<Foo> >(0, new Foo()));

}

Upvotes: 1

Related Questions