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
Related
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.
I have a container class like the following. As you can see that all the resources that the class use is allocated statically. There are no dynamically allocated resources in the class. Does such a class need a move constructor or move assignment operator?
template<class T, std::size_t SIZE>
class Stack{
static_assert(SIZE != 0, "Stack capacity cannot be zero!");
public:
/*** Constructors and Destructor ***/
Stack() = default; // Default constructor
Stack(const Stack& copyStack); // Copy constructor
~Stack(); // Destructor
/*** Member Methods ***/
/* .... */
void swap(Stack& swapStack);
private:
/*** Members ***/
std::size_t idxTop{0}; // Index after the top element
T data[SIZE]; // Contained data
};
For the ones who would like to try it out with the actual implementation:
Source code of Template Stack Container
Executable code on GodBolt
Here's the thing. Your class isn't movable, because it doesn't have dynamically allocated resources. But the resources it contains might. A T, for instance, might be a std::vector in some instantiation. That can surely be moved, so you have to make sure that by providing a copy constructor (you forgot to = default; it btw), the compiler doesn't accidentally disable the default move constructor.
Now, you should be careful about the default move constructor in your case. The problem is that you have an array as a member, and the default move constructor just moves the elements of the array one by one.
Except what happens when the move constructor of one of them throws? You end up with half of the elements moved, and some still in the old array because an exception was thrown. That's no good. Take a look at std::move_if_noexcept and perhaps try implementing the move constructor to fix this.
What exactly do you mean by need? I guess that you mean whether you need to write them for you class to stay moveable.
If you have an example like this:
#include <cstdint>
#include <utility>
class Foo
{
private:
std::size_t idxTop{0};
int data[4];
};
int main()
{
Foo f = Foo(); // Default ctor
Foo x = Foo(f); // Copy ctor
x = f; // Copy assignment
x = std::move(f); // Move assignment
Foo y = std::move(f); // Move ctor
}
The compiler will add
inline constexpr Foo() noexcept = default;
inline constexpr Foo(const Foo &) noexcept = default;
inline constexpr Foo(Foo &&) noexcept = default;
inline constexpr Foo & operator=(const Foo &) noexcept = default;
inline constexpr Foo & operator=(Foo &&) noexcept = default;
to the class's definition. I.e the class can be copied, moved freely as is shown in the main.
But - that's maybe what prompted your question - if you write a custom {copy con,des}structor for a class as you have done, the compiler will not put the defaulted definitions there. Why? Because by writing custom ones, you sent a signal that your class in some way special and default implementations would most likely not do the right thing.
So, your class is not moveable but remains copyable and copies will be used instead of move operations when required - e.g. the last two lines in my example.
I would suggest that whenever you customize one of those 5 functions, you define all of them, use default or delete if they suffice.
You can find more information about the default rules in the rule of three/five/zero.
No, your class does not need a move-ctor or move-assignment-operator.
You can't move static arrays, you can only copy their content. So a potential move-ctor would do the same as the copy-ctor. The same goes for assignment operators.
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
Simplified code:
#include <queue>
#include <memory>
#include <vector>
class Foo {
public:
Foo() {};
virtual ~Foo() {}
};
int main()
{
std::queue<std::unique_ptr<Foo>> queue;
auto element = std::make_unique<Foo>();
queue.push(std::move(element));
std::vector<std::queue<std::unique_ptr<Foo>>> vector;
// Error 1
vector.push_back(queue);
// Error 2
vector.push_back(std::move(queue));
// Error 3
vector.push_back({});
return 0;
}
Error:
'std::unique_ptr>::unique_ptr(const
std::unique_ptr<_Ty,std::default_delete<_Ty>> &)': attempting to
reference a deleted function
Obviously copying c~tor of unique_ptr is removed but I'm not trying to copy it. Am I?
This is a bit tricky. All std::vector<T> functions that can increase the size of the vector have to do it in an exception-safe way if either of these two things are true:
T has a move constructor that guarantees it will never throw any exceptions; or,
T has a copy constructor.
So in most implementations, if T has a move constructor declared nothrow or equivalent, vector will use the move constructor of T for those operations. If not, and T has a copy constructor, vector will use the copy constructor, even if T has a move constructor.
And the problem here is that std::queue always declares it has a copy constructor, even if that copy constructor can't actually be instantiated, and always declares it has a move constructor that might throw, even if the container member's move constructor guarantees it won't throw.
The Standard specifies these in [queue.defn] as:
namespace std {
template<class T, class Container = deque<T>>
class queue {
// ...
public:
explicit queue(const Container&);
explicit queue(Container&& = Container());
// ...
};
}
This class template definition could be improved in a couple of ways to be more "SFINAE-friendly" and avoid issues like the one you ran into. (Maybe somebody could check for other classes with similar issues and submit a proposal to the Library Working Group.)
Change the move constructor to promise not to throw if the Container type makes the same promise, typically done with language like:
explicit queue(Container&& rhs = Container()) nothrow(see below);
Remarks: The expression inside noexcept is equivalent to is_nothrow_move_constructible_v<Container>.
Change the copy constructor to be deleted if the Container type is not copyable, typically done with language like:
explicit queue(const Container&);
Remarks: This constructor shall be defined as deleted unless is_copy_constructible_v<Container> is true.
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).