I'm getting this weird error when inserting an element into std::unordered_map using emplace function but not if I use operator[] function overload. This is my code:
#include <iostream>
#include <unordered_map>
#include <memory>
class B {
public:
B() = default;
~B() = default;
};
class A {
public:
A() = default;
~A() = default;
std::unique_ptr<B> b_ptr;
};
int main() {
std::unordered_map<std::string, A> mp;
// mp.emplace("abc", A()); // gives compiler here
auto& a = mp["def"];
}
I'm getting huge error print when compiled. This is a short note of error: template argument deduction/substitution failed
When you use emplace like mp.emplace("abc", A()); what you are doing is creating a temporary A, and that object is then copied/moved into the object that emplace is going to construct. When you did ~A() = default; in the class, that gets rid of the compiler supplied default move constructor, and the copy constructor is implicitly deleted because std::unique_ptr can't be copied so the A() can't be moved or copied into the object emplace is going to create.
You can fix this by using the std::piecewise_construct taged version of emplace to forward the parts of the key value pair to emplace like
mp.emplace(std::piecewise_construct, // call the piecewise_construct overload
std::forward_as_tuple("abc"), // forwards "abc" to the key
std::forward_as_tuple()); // forwards nothing to the value so it can be default constructed
or you could just add a move constructor to A using
A(A&&) = default;
so that emplace can move the A() you created in main into mp.
If you are using C++17 or later, replacing
// mp.emplace("abc", A());
with
mp.try_emplace("abc");
compiles fine.
The reason why mp.emplace("abc", A()); produces a compile error is that the sub-expression A() constructs a new object of type A in main()'s scope and so emplace() expects to directly forward this new object as the argument into some A constructor, but there is no such constructor (said differently, somewhere inside the emplace() method's definition, there is something like the code A(A()), which is undefined).
As mentioned elsewhere, adding a move constructor to class A with the code A(A&&) = default; will also fix the compilation error (although this fix might not be an option with other, more complex classes). The reason why this removes the error is because there now exists a constructor that emplace() can forward this A() object into (as an argument). That is, the code A(A()) becomes well-defined.
Related
For inserting new elements std::map requires presence std::pair object.
I didn't find an alternative method to add a new element into std::map without constructing such std::pair object.
But suppose the std::map value is a heavy object consuming a lot of resources. Inserting such element into std::map constraints one unnecessary copy. In the case
I have some object X needed to be copied directly into std::map, std authors constrain me to perform this copy in two steps:
copy X->std::pair
copy pair obj -> std::map insert location.
How to eliminate these unneeded copy? Implementing Move constructor doesn't solve my problem - the copy still exists.
I suspect some method in std::map which inserts a new empty element with some key but no value, without calling any constructor for the value. Then I can move the value to thereby my own way.
But I don't find such method in std::map!
the std::map value is a heavy object consuming a lot of resources. Inserting such element into std::map constraints one unnecessary
copy. How to eliminate these unneeded copy?
You are right about this and std::map::emplace is the only solution what we have available now. A small explanation about the use of std::map::emplace from cppreference.com is given below:
Careful use of emplace allows the new element to be constructed while
avoiding unnecessary copy or move operations. The constructor of the
new element (i.e. std::pair<const Key, T>) is called with exactly the
same arguments as supplied to emplace, forwarded via
std::forward<Args>(args)....
That means the a class-instances can be constructed in-place at the time of map insert(i.e, instead of constructing and copying), via std::map::emplace, when you provide a contractor in the class with exactly same arguments. And then, you need to use std::map::emplace along with std::piecewise_construct and std::forward_as_tuple(assuming that the class contains more than one member).
myMap.emplace(
std::piecewise_construct,
std::forward_as_tuple(/*key of map*/),
std::forward_as_tuple(/*all the members which you want to constrct in place*/)
);
To demonstrate the above case, I have made a small sample code in which the members of ClassA will be constructed in place without calling any of the special member functions. To make it sure, I have disabled default, copy and move constructors.
SEE LIVE HERE
#include <iostream>
#include <map>
#include <tuple>
class ClassA
{
int _val;
std::string _str;
public:
explicit ClassA(const int val, const std::string& str) : _val(val), _str(str)
{
std::cout << "A class: C'tor called...!\n";
}
// disable the following
ClassA() = delete;
ClassA(const ClassA&) = delete;
ClassA& operator=(const ClassA&) = delete;
ClassA(ClassA&&) = delete;
ClassA& operator=(ClassA&&) = delete;
};
class ClassB
{
ClassA _aObj;
public:
explicit ClassB(const int val, const std::string& str) : _aObj(val, str)
{
std::cout << "B class: C'tor called...!\n";
}
// disable the following
ClassB() = delete;
ClassB(const ClassB&) = delete;
ClassB& operator=(const ClassB&) = delete;
ClassB(ClassB&&) = delete;
ClassB& operator=(ClassB&&) = delete;
};
int main()
{
std::map<int, ClassB> myMap;
myMap.emplace(
std::piecewise_construct,
std::forward_as_tuple(1),
std::forward_as_tuple(1, "some string")
);
return 0;
}
Output:
A class: C'tor called...!
B class: C'tor called...!
Update: On the other hand,
The element may be constructed even if
there already is an element with the key in the container, in which
case the newly constructed element will be destroyed immediately.
That means, your expectation(or assumption) of:
" I suspect some method in std::map which inserts a new empty element with some key but no value, without calling any constructor for the value. Then I can move the value to thereby my own way. "
is not possible to achieve, by above mentioned std::map::emplace way. As #aschepler pointed out in the comments, you can have maps with sometimes(optional) keys and no values by using the C++17 feature std::optional.
For that, you need to make the values as optional as follows and of course the compiler version support C++17 or later.
std::map<Key, std::optional<Value>> myMap;
Now you can construct an object anytime in the code and after a while, you can move it to appropriate key-value(i.e, entry). Last but not least, don't forget to provide default move-c'ntors
SEE SAMPLE CODE HERE
#include <optional>
class ClassA {
/* same as before*/
public:
// disable the copy c'tor
// enable move c'ntors
ClassA(ClassA&&) = default;
ClassA& operator=(ClassA&&) = default;
};
class ClassB {
/* same as before*/
public:
// disable the copy c'tor
// enable move c'ntors
ClassB(ClassB&&) = default;
ClassB& operator=(ClassB&&) = default;
};
int main() {
std::map<int, std::optional<ClassB>> myMap;
// created without calling any constructors
myMap.emplace(1, std::nullopt);
//later in the code
ClassB bObj{1, "JeJo"};
// again after..... move it to the required key-value
myMap[1] = std::move(bObj);
return 0;
}
Output:
A class: C'tor called...!
B class: C'tor called...!
You are looking for std::map::emplace
https://en.cppreference.com/w/cpp/container/map/emplace
#include <memory>
template <typename T>
class Wrapper {
public:
Wrapper() = delete;
Wrapper(const Wrapper&) = delete;
Wrapper(Wrapper&&) = delete;
~Wrapper() = default;
Wrapper(const T&) = delete;
Wrapper(T&& in) : instance{std::move(in)} {}
T instance;
};
void foo(Wrapper<std::shared_ptr<int>>) {}
int main() {
auto ptr = std::make_shared<int>(1);
foo(std::move(ptr));
}
This has been working in C++17 so I never gave it thought but why does this code try and invoke the move constructor in C++14? Shouldn't it be constructed in place in the function argument? This seems to not be a problem with c++17 but is not compiling with c++14.
The only workaround I see is to make the foo parameter an rvalue, but is there anything I can do to make this work without making the parameter in foo an rvalue in C++14?
My first thought would be that a temporary would have to be constructor in order to be passed to the function but what is even more surprising is that even with -fno-elide-constructors and undeleting the move constructors and copy constructors those do not seem to be called! Is this a bug in gcc and clang both?
See https://wandbox.org/permlink/f6sa5Rm3NxZLy5P1 for the error
And see for the strange behavior https://wandbox.org/permlink/Kh6CG4OVbUAjvEZz
When you call foo(std::move(ptr)); you are not giving it a Wrapper<std::shared_ptr<int>>. So, the compiler generates a temporary and uses that to construct foo's parameter. Now, this can be elided out and we can directly construct a Wrapper<std::shared_ptr<int>> but the move/copy constructor still needs to be accessible, even if it is never called.
With C++17 this no longer happens. We have guaranteed copy elision which means no temporary is ever materialized and instead the parameter is directly constructed.
Below is the minimalist problem of the code:
struct B {
B () = default;
//~B () {}; // error: use of deleted function ‘B& B::operator=(const B&)’
std::unique_ptr<int> m_pB = nullptr;
};
int main ()
{
std::vector<B> vB;
vB.erase(vB.begin());
}
Above code compiles fine, unless the destructor is uncommented. For my requirement, I need to have a body of ~B() explicitly defined.
How can I define the body of destructor with the unique_ptr co-existing in the same class?
Note: Tried defining = default versions of copy & move constructor to no avail. In my real code, unique_ptr<int> is unique_ptr<forward_declared_class>. Couldn't locate this problem in SO, though I am sure it must be present. Feel free to mark as dupe.
Seems like your code requires B to be copy constructible and copy assignable for std::vector (at least for visual c++, which I tested).
Copy constructor and copy assignement operators can only be = delete because of std::unique_ptr (implementations declared = default should cause the function to be deleted as well), and, by implementing destructor, you disable default implementations of move constructor and move assignment operator.
So you need to explicitly declare the move assignment operator. Try:
#include <memory>
#include <vector>
struct B {
B () = default;
~B () {}
B& operator=(B&&) = default;
std::unique_ptr<int> m_pB = nullptr;
};
int main ()
{
std::vector<B> vB;
vB.erase(vB.begin());
}
You might declare and implement a move-assignment operator, which the compiler asks for. Of course, it complains about copy assignment, but a sensible copy seems to be impossible for your structure.
Probably, the error message is a bit misleading because std::vector::erase implementation contains something like _data[i-1] = _data[i] in the code which pokes vector elements to places of deleted items. So the compiler needs any assignment operator, and we provide it with a moving one.
For example, this works fine (gcc 4.8.3):
struct B {
B () = default;
B& operator=(B&& op) {
m_pB = std::move(op.m_pB);
return *this;
}
~B () {}; // no more errors.
std::unique_ptr<int> m_pB = nullptr;
};
int main ()
{
std::vector<B> vB;
vB.erase(vB.begin());
}
The vector code is a red herring.
When a class U has a member of type unique_ptr, and T is forward declared, the definition of the destructor has to be in the cpp file, when U is no longer an incomplete type.
You should have ~U(); in the header, and in the cpp file have U::~U(){} or U::~U() = default;
I have a type that I have deleted the copy constructor from, and I would like to have a vector of this type, so I need to create all the elements via emplace_back. But, emplace_back seems to require a copy constructor, as the compiler gives a warning about not being able to instantiate emplace_back because the copy constructor has been deleted. Why does it need a copy constructor? I thought the whole point of emplace_back was to build the vector without copying anything. Can I even have a vector of objects that don't have a copy constructor?
class MyType {
public:
MyType(std::array<double, 6> a) {}
MyType(const MyType& that) = delete;
};
int main() {
std::vector<MyType> v;
std::array<double, 6> a = {1,2,3,4,5,6};
v.emplace_back(a);
}
Compiler is clang/llvm.
When the vector's internal storage grows, it will need to move the elements from the old storage to the new. By deleting the copy constructor, you also prevent it generating the default move constructor.
To be able to call emplace_back, your type should either be EmplaceConstructible or MoveInsertible .
You need to give a move constructor to your class if you have deleted the copy constructor. (Check this for requirements of emplace_back)
MyType(MyType &&a) {/*code*/} //move constructor
If you try to run this code:
// Example program
#include <iostream>
#include <string>
#include <array>
#include <vector>
class MyType {
public:
MyType(std::array<double, 6> a) {
std::cout<< "constructed from array\n";
}
MyType(const MyType& that){
std::cout<< "copy\n";
}
MyType(MyType&& that){
std::cout<< "move\n";
}
};
int main() {
std::vector<MyType> v;
std::array<double, 6> a = {1,2,3,4,5,6};
v.emplace_back(a);
}
You will get the following result:
constructed from array
Live Demo
It is clear that just the constructor from std::Array is called. So, no need for copy constructor. But in the same time if you deleted the copy constructor, the compiler will raise an error (at least on two compilers I tried first second ). I think that some compilers will check for the existence of copy constructor when using emplace_back even if it is not necessary in this practical case while others won't. I do not know what is standard here (which compiler is right or wrong).
I want to use vector::emplace to default construct a non-copyable and non-assignable object and then use specific methods on the object using an iterator to the newly created object. Note that there are no parameterized constructors of the class just the default constructor. A simple example is:
#include <iostream>
#include <vector>
using namespace std;
class Test {
public:
Test() {}
private:
Test(const Test&) = delete; // To make clas un-copyable.
Test& operator=(const Test&) = delete;
int a_;
};
int main() {
vector<Test> test_vec;
test_vec.emplace_back(); // <---- fails
return 0;
}
vector::emplace() constructs a new object but requires arguments to a non-default constructor. vector::emplace_back() will construct at the end of the vector.
Is there a way to emplace with default construction. Is there a way to use piecewise construction or default forwarding perhaps using std::piecewise_construct as there is for maps? For example, in the case of maps, we can use:
std::map<int,Test> obj_map;
int val = 10;
obj_map.emplace(std::piecewise_construct,
std::forward_as_tuple(val),
std::forward_as_tuple());
Is there something similar for vectors?
vector::emplace_back() will construct at the end of the vector but also require arguments.
Parameter packs can be empty. Thus the variadic template emplace_back can be called without arguments; I.e.
vector<VeryLimitedClass> vec;
vec.emplace_back();
Is valid code that initializes an object of type VeryLimitedClass through its default constructor and "emplaces" it at the back of vec.
As pointed out by #dyp and #Casey in the comments, std::emplace will not work for vectors of the Test class as the class is neither movable nor copyable because "the user-declared copy constructor suppresses generation of the default move constructor" (#Casey).
To use emplace here, the class will need to be movable or copyable. We can make the class movable by explicitly defining (and defaulting) the move constructors:
public:
Test(Test&& other) = default;
Test& operator=(Test&& other) = default;
This will also implicitly make the class not-copyable "since declaring the move operations will suppress implicit generation of the copies." (#Casey)
Now we can use std::emplace_back() and then use vector::back() to call methods of the newly created object.
For map, easy:
std::map<int, Object> obj_map;
obj_map[10]; // default-constructs an object with key 10
Otherwise, what you have works too:
obj_map.emplace(std::piecewise_construct,
std::forward_as_tuple(10),
std::forward_as_tuple(args, to, construct, with));
[edit] The equivalent for vector is emplace_back:
obj_vector.emplace_back(); // default construct
obj_vector.emplace_back(args, to, std::move(construct), with); // forward these