Reputation: 11580
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
Reputation: 20141
Instances of classes without a usable copy constructor / copy assignment can still be used in std::map
using a little special trick:
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()
Upvotes: 1
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
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