1

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

My setup

  • Visual Studio 2017
  • Windows 10

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!

10
  • What is the role of MyFoo and YourFoo in the 2nd example? Commented Sep 17, 2019 at 5:23
  • 1
    What compiler do you use? It works on my Visual Studio 2017 on Windows 10. Commented Sep 17, 2019 at 5:29
  • compile same code successfully in VS2017 on windows 10 Commented Sep 17, 2019 at 5:41
  • @Scheff So you mean the errors may well be bogus .. I've seen many bogus error messages, but this one really got me. Indeed, I must incrementally change things until I find the difference between the code bases. Commented Sep 17, 2019 at 6:09
  • @PiotrSiekański It's the VS2017 one. Funny thing is the second example works with the same compiler. Commented Sep 17, 2019 at 6:14

3 Answers 3

1

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

Sign up to request clarification or add additional context in comments.

Comments

1
#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()));

}

1 Comment

Ok. So without modifying the constructor schemes, the only way is to use dynamic allocation through shared pointers?
0

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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.