To enable move semantics on a std::vector do I need to pass by value; so the compiler will not copy the element but just move them?
Example:
class data
{
public:
void setMyData(vector<string> sourceData)
{
private_data_ = sourceData;
}
private:
vector<string> private_data_;
};
to enable move semantics on a C++ stl vector
You have misunderstanding about the move semantic concept.
The std::vector itself is move constructable as it has the move constructor defined. You do not need to enable it explicitly, rather use it properly.
In the function
void setMyData(vector<string> sourceData) // copy 1
{
private_data_= sourceData; // copy 2
}
You are doing double copy. You need instead
void setMyData(vector<string> sourceData) // copy
{
private_data_= std::move(sourceData); // move
}
or you might want
void setMyData(vector<string>&& sourceData) // only accept the rvalue
{
private_data_= std::move(sourceData); // move
}
and for the second case, you call the function
setMyData(std::move(sourceData));
I would recommend either using 2 overload of that function or even use a generic one:
#include <vector>
#include <string>
class data
{
public:
void setMyData(const std::vector<std::string>& sourceData){private_data_=sourceData;}
void setMyData(std::vector<std::string>&& sourceData){private_data_= std::move(sourceData);}
template <typename T>
void genericsetMyData(T&& source) {private_data_ = std::forward<T>(source);}
private:
std::vector<std::string> private_data_;
};
int main() {
class data a,b,c,d;
std::vector<std::string> s1,s2; const std::vector<std::string> s3;
a.setMyData(s1);
b.setMyData(std::move(s2));
c.genericsetMyData(s1);
d.genericsetMyData((std::move(s1)));
d.genericsetMyData(s3);
}
The templated one is a bit more complex since it uses a so called fowarding reference.
I suggest the following:
#include <utility>
class data
{
public:
void setMyData(vector<string> sourceData)
{
private_data_ = std::move(sourceData);
}
private:
vector<string> private_data_;
};
And to init, you have two ways:
vector<string> myStrings{"hello", "i am", "stringies"};
data mydata;
// Type 1: Give ownership
mydata.setMyData(std::move(myStrings)); // not a single copy, but myStrings is empty
// Type 2: Copy
mydata.setMyData(myStrings); // Copy once only, and myStrings is still valid
The good thing about this technique is that you don't have to write several overloaded methods, you can choose which way you want to pass your parameter.
Related
I am working on a "big" project and I am stuck with a segmentation fault which I finally narrowed to a simpler and reproducible example:
I am going to put two pieces of code. First:
#include <iostream>
#include <string>
#include <vector>
using namespace std;
int main()
{
std::vector<int> someVector;
printf("1\n");
someVector.push_back(1);
printf("2\n");
someVector.push_back(2);
printf("3\n");
someVector.push_back(3);
someVector.erase(someVector.begin());
someVector.erase(someVector.begin());
return 0;
}
Here, everything works fine. Ok. Now let's try something a little more complicated. Instead of using a vector of ints, lets use a vector of my own defined class.
Note: I'm no expert and I may have made errors when defining the classes. The reason why there is a copy constructor in the class definition is because the parameters variable, is actually a pointer to another class (and not an int), which I copy on the body of the constructor on the real example.
#include <iostream>
#include <string>
#include <vector>
using namespace std;
class ActionType {
public:
std::string name;
int type;
int * parameters;
ActionType(const std::string _name, const int _type
);
ActionType(const ActionType &);
~ActionType(){
// Direct implementation of destructor
printf("DELETING\n");
delete parameters;
}
};
// Implementation of initializer
ActionType::ActionType(const std::string _name, const int _type)
: name(_name)
, type(_type)
, parameters(new int(5))
{
}
// Implementation of copy constructor
ActionType::ActionType(const ActionType & actionType)
: name(actionType.name)
, type(actionType.type)
, parameters(new int(*actionType.parameters))
{
printf("COPYING\n");
}
int main()
{
ActionType actionType("foo", 1);
std::vector<ActionType> actions;
printf("1\n");
actions.push_back(actionType);
printf("2\n");
actions.push_back(actionType);
printf("3\n");
actions.push_back(actionType);
actions.erase(actions.begin());
actions.erase(actions.begin());
return 0;
}
This, which should be pretty much the same, throws the error:
*** Error in `./a.out': double free or corruption (fasttop): 0x0000000001618c70 ***
Aborted (core dumped)
The thing is, in my real example I need a way to delete several items of the vector, and to do that I have something like:
for (int i (...)){
int indexToDelete = getIndex();
vector.erase(vector.begin()+indexToDelete);
}
I could also use something like:
std::vector<int> indexes;
for (int i (...)){
int indexToDelete = getIndex();
indexes.push_back(indexToDelete);
}
vector.erase(indexes);
But I haven't figured any function that does this. I've seen other answers where they use sort to put all items to remove at the end and after they call pop_back, but it didn't seem super clean.
Anyway, in summary:
Does somebody know why when using a class with vector the function erase doesn't work as good?
How can I delete several items knowing their indexes in a vector?
You are missing the assignment operator for your class. The std::vector requires that your class has correct copy semantics, and your ActionType does not have correct copy semantics.
It is simple to add an assignment operator for the class you've written:
#include <algorithm>
//...
class ActionType
{
//...
ActionType& operator=(const ActionType &);
//...
};
ActionType& ActionType::operator=(const ActionType &rhs)
{
if ( this != &rhs )
{
ActionType temp(rhs);
std::swap(temp.name, name);
std::swap(temp.type, type);
std::swap(temp.parameters, parameters);
} // <-- The temp is destroyed here, along with the contents.
return *this;
}
The above uses the copy / swap idiom to implement the assignment operator.
Note that the code now works when the assignment operator is added.
Edit:
The alternate form of the assignment operator could be:
class ActionType
{
//...
ActionType& operator=(ActionType);
//...
};
ActionType& ActionType::operator=(ActionType temp)
{
std::swap(temp.name, name);
std::swap(temp.type, type);
std::swap(temp.parameters, parameters);
return *this;
}
When you delete the parameters array you need to supply a []
delete [] parameters;
Otherwise you get undefined behavior in your program.
Sidenote > It would probably better to keep it as a std::vector or std::array instead.
I need to create a simple template container, which can store any object of any type and could be used everywhere. So, I did something like this:
template <typename Type>
class Container {
public:
Container() : arraySize(10) { valueWrappers = new Type[arraySize];}
Container(const Container& other) { /* --- */}
~Container() { /* --- */}
Container& operator=(const Container& other) { /* --- */}
/* some functions */
private:
int arraySize;
Type* valueWrappers;
};
Now I have the problem - when I'm trying to create my container using as template a class without default constructor, the compilation error appears:
class MyClass {
public:
MyClass(int value) :v(value) { }
private:
int v;
};
int main() {
Container<MyClass> cont;
return 0;
}
C2512 'MyClass': no appropriate default constructor available
The problem is that I need to initialize the array of "Type" values with something, but I don't no what I need to use. I can't use NULL because, in this case, Container will work only with pointers. So, can somebody give an advice, how am I able to do it? Or, maybe, there is another way to solve this task?
Based on your requirements, I think you're going to have to use placement new. Since you haven't provided all the relevant code, I'm going to do what I can.
First, you're going to have to allocate raw memory instead of using new directly.
Container() : arraySize(10) { valueWrappers = reinterpret_cast<Type*>(::operator new(sizeof(Type) * arraySize)); }
Now when you put something in your Container, you'll have to construct it in place, using something like the following:
new (valueWrappers + index) Type(arguments to type);
In your destructor, you'll need to explicitly call the destructors on any object that you used placement new for.
valueWrappers[index]->~Type();
Lastly, release the memory using ::operator delete.
::operator delete(valueWrappers);
Please bear in mind that this is a very quick and dirty answer, and this code can be hard to debug and maintain. You're going to have to keep track of what indexes in valueWrapper have been initialized and which haven't during cleanup. If possible, I highly recommend using something akin to std::vector, which handles all this complexity for you.
One option is to not allocate the array in the default constructor, but initialise valueWrappers to null instead. Another option is to not have a default constructor in your template. Third option is to keep your class as-is and simply document that the template is default constructible only if the type argument is default constructible.
You can use std::optional to defer initialization, which is guaranteed to handle object lifetime correctly. Letting a default constructed container have 10 elements is also a questionable choice — a (count) constructor may be preferable.
template <typename Type>
class Container {
using elem_t = std::optional<Type>;
std::size_t count{};
std::unique_ptr<elem_t[]> elems{};
public:
Container() = default;
Container(std::size_t cnt)
: count{cnt}
, elems{std::make_unique<elem_t[]>(cnt)}
{
}
// for example
template <typename... Args>
void construct_at(std::size_t pos, Args&&... args)
{
assert(pos < count);
assert(!elems[pos]);
elems[pos].emplace(std::forward<Args>(args)...);
}
// ...
};
Note that I used std::unique_ptr to simplify memory management; a pointer will also be OK, though apparently more error-prone. Now you can traverse the container and construct the elements:
class MyClass {
public:
MyClass(int value) :v(value) { }
private:
int v;
};
int main()
{
Container<MyClass> cont(10);
for (std::size_t i = 0; i < 10; ++i) {
cont.construct_at(i, /* argument */);
}
}
Like if I have this structure:
struct S
{
S(const S &arg) : (arg.bIsDouble ? v1{arg.v1} : v{arg.v}) {}
bool bIsDouble{false};
union {
vector<int> v;
double v1;
};
} ;
How can I make the copy constructor to either initialize 'v' or 'v1' based on some condition?
I would just outsource your work to Boost.Variant:
struct S {
S(const S&) = default;
boost::variant<double, std::vector<int> > v;
};
If you don't want to use Boost, you can read about how to write a discriminated union here, and then implement your own.
Since you only need two types, that's not too complicated, although it's still very error prone and involves a lot of code:
struct DoubleOrVector {
bool is_double;
static constexpr std::size_t alignment_value = std::max(alignof(double), alignof(std::vector));
alignas(alignment_value) char storage[std::max(sizeof(double), sizeof(std::vector))];
DoubleOrVector(double v)
: is_double(true)
{
new (storage) double(v);
}
// similar for vector
DoubleOrVector(const DoubleOrVector& dov)
: is_double(dov.is_double)
{
if (is_double) {
new (storage) double(dov.asDouble());
}
else {
new (storage) std::vector<int>(dov.asVector());
}
}
double& asDouble() {
assert(is_double);
return *reinterpret_cast<double*>(storage);
}
std::vector<int>& asVector() {
assert(!is_double);
return *reinterpret_cast<std::vector<int>*>(storage);
}
// plus other functions here
// remember to explicitly call ~vector() when we need to
};
And then we're still defaulting our copy ctor:
struct S {
S(const S&) = default;
DoubleOrVector v;
};
Constructor initialization list won't help here.
You have to use placement new in the class constructor, and then destroy (by manually calling the destructor) the correct member in the destructor. Also, since you define the destructor, you should define or delete the rest of The Big Five.
Minimal code:
struct S
{
bool bIsDouble{false};
union {
vector<int> v;
double v1;
};
S(bool d) : bIsDouble(d)
{
if(!bIsDouble)
new(&v) vector<int>();
// no need to create or destroy a double, since it is trivial
}
~S()
{
if(!bIsDouble)
v.~vector<int>();
}
// the other Big Five members are implicitly deleted
// so no copying, unless you write it yourself.
};
Note that switching between types is harder: if you had used the vector, and now want to use double, you need to destroy the vector first. I recommend to hide the data behind the accessor functions to enforce the invariant.
...or just use boost::variant. It's way simpler and less bug-prone.
I build a class for containing vectors with no default constructor. Specifically:
template<typename T>
struct MyVector
{
public:
int GetN(void)
{
return n;
}
MyVector(int n1)
{
n=n1;
ListElt = new T[n1];
}
~MyVector()
{
delete [] ListElt;
}
// some other irrelevant to the question code
private:
int n;
T *ListElt;
};
Now I want to build a class derived from it that contains one integer and a vector.
The code that works is the following:
struct EquivInfo {
public:
EquivInfo(int inpOrbit, MyVector<int> &inpMat)
{
iOrbit=inpOrbit;
eVect=new MyVector<int>(inpMat.getN());
// some code for copying the vector in place.
}
~EquivInfo()
{
delete eVect;
}
private:
int iOrbit;
MyVector<int> *eVect;
};
Is there a way to avoid the use of the pointer for the vector?
The problem is that if I remove the pointer then there is a call to a constructor of the kind MyVector(). I do not understand why. There should be a way to have a constructor for EquivInfo that calls a precise constructor for MyVector
I could add a constructor MyVector(), i.e. a default constructor that set the vector to something trivial. But precisely, I want to avoid such kind of constructors so that all vectors are well defined and the code is clean. The use of the pointer allows me to have a reasonable situation but I wonder if there is a clean way to avoid it.
Use member initializer list:
class EquivInfo {
public:
EquivInfo(int inpOrbit, MyVector<int> &inpMat)
: eVect(inpMat.getN())
, iOrbit(inpOrbit) {
// some code for copying the vector in place.
}
// ....
MyVector<int> eVect;
}
I have a class template S<T> and because the template parameter is sometimes hard write explicitly I also have a little helper function makeS(...) to deduce the template parameter.
Now the problem is that the constructor of S has a "side effect": it adds itself to a map which then will later be used to iterate over all instances of S. But this effectively makes S<T> s{...}; very different from auto s = makeS(...); (if RVO is not used).
Here is some code which hopefully shows what I'm trying to do. (Note: in the actual program, S has more than a single template parameter and all will be deduced in makeS)
#include <cassert>
#include <iostream>
#include <map>
#include <string>
#include <utility>
using namespace std;
struct Base
{
virtual ~Base() {}
virtual void f() const = 0;
};
static map<string, Base*> Map;
template <typename T>
struct S : Base
{
T Func;
Base* This;
S(string const& name, T func) : Func(std::move(func)), This(this)
{
//
// Automatically add this struct to Map...
//
Map.insert({name, this});
}
virtual void f() const override { Func(); }
};
template <typename T>
S<T> makeS(std::string const& name, T func)
{
return S<T>(name, std::move(func));
}
void func1()
{
std::cout << "func1\n";
}
int main()
{
struct Func2
{
void operator ()() const {
std::cout << "func2\n";
}
};
//
// This is not possible:
//
// S< ??? > s("s", [](){});
//
// This would be ok:
//
// auto F = [](){};
// S<decltype(F)> s("s", F);
//
auto s1 = makeS("s1", func1);
auto s2 = makeS("s2", Func2());
auto s3 = makeS("s3", [](){ std::cout << "func3\n"; });
//
// Manually adding s1,... to the Map is ok, but that's what
// I want to avoid...
//
// Map.insert({"s1", &s1});
// ...
//
assert(&s1 == s1.This);
assert(&s2 == s2.This);
assert(&s3 == s3.This);
for (auto&& I : Map)
{
I.second->f();
}
}
As I understand it, the map will only contain valid pointers if RVO is used in auto s1 = makeS(...) etc. and this is not guaranteed.
Is there a way to deduce the template parameters while at the same time avoiding the need to manually register s1,...?
Your basic problem is you failed to implement the rule of 3. If your destructor needs non-trivial behavior (and if you register yourself in the constructor, this is the case), you must either implement or block assignment and copy construct (and/or move-assign and move-construct).
In this case, we can implement a move-construct and block move-assign, and copy construct and copy assign are implicitly blocked.
First, add name to S. Then implement a move constructor.
template <typename T>
struct S : Base
{
std::string Name;
T Func;
Base* This; // ?? why ?? this looks both dangerous and useless at the same time!
S( S&& s ): Name(std::move(s.Name)), Func(std::move(s.Func)), This(this) {
s.clear(); // technically `move` need not clear.
map[Name] = this; // overwrite
}
S& operator=(S&& s) = delete; // or implement it
now your object is moveable, and when moved it updates the Map. ~S should, I assume, unregister from Map -- detect if your name is empty (and assert at construction you gain a non-empty name), and if it is don't unregister (as you where already moved from).
Now move-construct and elided-construct have the same semantics. RVO failure results in some inefficiency, but no logical failure. Plus, your type is now moveable, which tends to be really useful.
If you need to maintain object identity, use can use std::unique_ptr:
template <typename T>
std::unique_ptr<S<T>> makeS(std::string const& name, T func)
{
return { new S<T>(name, std::move(func)) };
}
Now moving the pointer from place to place won't move the object; the pointer kept in the map will stay valid.
My suggestions to improve your code:
1) Get rid of the side effect in the constructor. Create objects in a factory method only (makeS in your code; you can make it a static member function of S) and register the S objects in that method. To disable object creation in different ways make constructor(s) private.
2) Disable S objects copying/moving and handle the objects for example as shared_ptr/unique_ptr<S> only. When you disable copying/moving you avoid the problem when your map contains a pointer to invalid objects and now you don't need to rely on RVO.
3) Use std::function<void()> instead of T Func;. In that case your class don't need to be a template class (or it will have less template arguments). That will simplify your code.