boost::shared_* with copy constructor and assignment operator - boost

I have a class that contains a boost::shared_array member. The other members are not dynamic - just a bunch of ints, no pointers. I would expect that the default copy constructor for such a class would be fine.
This is my assumption:
Let's say I have an instance of this class, orig.
orig's shared_array member has a reference count of 1.
Now I create a copy of orig:
copy = orig;
I now expect both copy and orig to have shared_arrays that point to the same underlying memory, each with a reference count of 2.
Is the above correct?
I'm intimidated by various people who warn against the default copy constructor when there is a boost::shared_* member - but I can never find an explanation why the default would/could be bad. For example, here's a comment by someone who says an explicit copy/assignment should be defined, but no explanation why:
https://stackoverflow.com/a/716112/629530
Can someone clarify when a copy constructor and assignment operator need to be defined for a class that contains boost::shared_* (shared_array and shared_ptr) members?

The following class uses the Pimpl Idiom in combination with a shared_ptr:
class location
{
struct impl
{
double _latitude;
double _longitude;
};
std::shared_ptr<impl> _impl;
public:
location(double latitude, double longitude)
: _impl{new impl{latitude, longitude}}
{ }
void move_to(double latitude, double longitude)
{
_impl->_latitude = latitude;
_impl->_longitude = longitude;
}
// ...
};
The code compiles and works. However, there is a strange behaviour:
location london{51.51, 0.12};
location paris = london;
paris.move_to(48.86, 2.35);
std::cout << "London: " << london << '\n'; // prints 48.86/2.35
Changing the copy of an object also affected the original object. In this case, it is better to use std::unique_ptr instead of std::shared_ptr, because we would have been forced to write our own copy constructor.
There are also cases in which the behaviour of std::shared_ptr is desired. Let's add a member variable metric that will be used to calculate the distance between two locations (because there might be different strategies for calculating distances):
std::shared_ptr<metric> _metric;
double operator-(const location& rhs) const
{
return _metric->distance(*_impl, *rhs->_impl);
}
In this case a std::shared_ptr works perfectly, because the metric is not part of the perceived state of the location, and there is no way to change the metric.

Related

Explicit invocation of ctor for allocation of memory

Consider the following class:
class Vector{
int dim; //dimension of array v
Complex* v; //Complex is another class
public:
Vector(int dim = 0):dim(dim){(dim)?(v=new Complex[dim]):(v=nullptr);}
Vector(int dim, const Complex* c):dim(dim),v(new Complex[dim]){
for(int i=0;i<dim;i++) v[i]=c[i];}
Vector(const Vector& a):dim(a.dim),v(new Complex[a.dim]){
for(int i=0;i<dim;i++) v[i]=a.v[i];}
~Vector(){if(dim)delete [] v,v=nullptr;}
friend Vector& operator >> (Vector& is,Complex& z){
Vector copie(is);
is.~Vector();
is.Vector::Vector(is.dim+1);}
};
I try to overload the >> operator in order to add elements to v.
My idea was to create a copy, then call dctor and the ctor for the object to
be modified via >> operator.
I'm stuck after getting this error:
In function ‘Vector& operator>>(Vector&, Complex&)’:
main.cc:56:20: error: cannot call constructor ‘Vector::Vector’ directly
is.Vector::Vector(is.dim+1);
I'm not allowed to use containers!!
Please help me!
That's right, you can't call the constructor directly. Probably you want to use placement new.
friend Vector& operator >> (Vector& is,Complex& z){
Vector copie(is);
is.~Vector();
// is.Vector::Vector(is.dim+1);
new(&is) Vector(is.dim + 1);
return is;
}
Even then the code may not be semantically correct.
Having said that, this is not the recommended way to do it for
the last 20 years. Watch this Jon Kalb "Exception-Safe Code, Part
I" for an explanation (the example is almost the same). The
recommended way is to implement this in terms of other operations like
copy or swap.
Minor syntactic detail, operator>> is confusing, use operator<< at worst.
There is no need for calling the destructor and calling the constructor. Steps you can take to make your function work:
Allocate memory to hold the current objects plus the additional object.
Copy the objects from the old memory location to the new memory location.
Delete the old memory.
Associate the newly allocated memory with the input object.
friend Vector& operator>>(Vector& is, Complex& z){
// Allocate memory
Complex* vnew = new Complex[dim+1];
// Copy objects to new memory.
std::copy(is.v, is.v + is.dim, vnew);
vnew[is.dim] = z;
// Delete the old memory.
delete [] is.v;
// Use the new memory
is.v = vnew;
// Increment dim.
is.dim++;
return is;
}
Having said that, I think you are using the wrong function to insert an element to Vector. operator>> is for extracting data from. operator<< is for inserting data to. You should use operator<< to insert an element to a Vector.
friend Vector& operator<<(Vector& is, Complex const& z){
...
}

Is there a way to make a moved object "invalid"?

I've some code that moves an object into another object. I won't need the original, moved object anymore in the upper level. Thus move is the right choice I think.
However, thinking about safety I wonder if there is a way to invalidate the moved object and thus preventing undefined behaviour if someone accesses it.
Here is a nice example:
// move example
#include <utility> // std::move
#include <vector> // std::vector
#include <string> // std::string
int main () {
std::string foo = "foo-string";
std::string bar = "bar-string";
std::vector<std::string> myvector;
myvector.push_back (foo); // copies
myvector.push_back (std::move(bar)); // moves
return 0;
}
The description says:
The first call to myvector.push_back copies the value of foo into the
vector (foo keeps the value it had before the call). The second call
moves the value of bar into the vector. This transfers its content
into the vector (while bar loses its value, and now is in a valid but
unspecified state).
Is there a way to invalidate bar, such that access to it will cause a compiler error? Something like:
myvector.push_back (std::move(bar)); // moves
invalidate(bar); //something like bar.end() will then result in a compiler error
Edit: And if there is no such thing, why?
Accessing the moved object is not undefined behavior. The moved object is still a valid object, and the program may very well want to continue using said object. For example,
template< typename T >
void swap_by_move(T &a, T &b)
{
using std::move;
T c = move(b);
b = move(a);
a = move(c);
}
The bigger picture answer is because moving or not moving is a decision made at runtime, and giving a compile-time error is a decision made at compile time.
foo(bar); // foo might move or not
bar.baz(); // compile time error or not?
It's not going to work.. you can approximate in compile time analysis, but then it's going to be really difficult for developers to either not get an error or making anything useful in order to keep a valid program or the developer has to make annoying and fragile annotations on functions called to promise not to move the argument.
To put it a different way, you are asking about having a compile time error if you use an integer variable that contains the value 42. Or if you use a pointer that contains a null pointer value. You might be succcessful in implementing an approximate build-time code convention checker using clang the analysis API, however, working on the CFG of the C++ AST and erroring out if you can't prove that std::move has not been called till a given use of a variable.
Move semantics works like that so you get an object in any it's correct state. Correct state means that all fields have correct value, and all internal invariants are still good. That was done because after move you don't actually care about contents of moved object, but stuff like resource management, assignments and destructors should work OK.
All STL classes (and all classed with default move constructor/assignment) just swap it's content with new one, so both states are correct, and it's very easy to implement, fast, and convinient enough.
You can define your class that has isValid field that's generally true and on move (i. e. in move constructor / move assignment) sets that to false. Then your object will have correct state I am invalid. Just don't forget to check it where needed (destructor, assignment etc).
That isValid field can be either one pointer having null value. The point is: you know, that object is in predictable state after move, not just random bytes in memory.
Edit: example of String:
class String {
public:
string data;
private:
bool m_isValid;
public:
String(string const& b): data(b.data), isValid(true) {}
String(String &&b): data(move(b.data)) {
b.m_isValid = false;
}
String const& operator =(String &&b) {
data = move(b.data);
b.m_isValid = false;
return &this;
}
bool isValid() {
return m_isValid;
}
}

How can I push back onto a vector an instance of a class in C++

Bear with me please, this is my first time posting. I have 3 classes. Class Suppliers has a set of Class Parent. Class Parent has a vector of Class location and Class location has data memebers. Ex (this is pseudo code, not my actual code. I've only shown this for simplicity sake):
Class Suppliers{
set<Parent> setter;
};
Class Parent{
vector<location> loc;
};
`
The following is the a constructor of the location class I created. I run into no problems until lines I hit the two lines with the iterators. I am trying to find a specific Parent and push back a new location onto the Parent 'loc' vector. So I pass in the iterator I've found previously as a reference. But as soon as I try to push back the new instance of my location class I get the following error.
data.cpp:139:33: error: passing 'const std::vector' as 'this' argument of 'void std::vector<_Tp, _Alloc>::push_back(const value_type&) [with _Tp = location; _Alloc = std::allocator; std::vector<_Tp, _Alloc>::value_type = location]' discards qualifiers [-fpermissive]
The last line also gives an error that says I cannot alter a read-only object. I'm confused as to why this would be a read only object. I did some research and thought that I needed to add a copy constructor. However, that caused more problems than it solved. Any ideas?
location::location(set<Parent>::iterator &it, vector<string> v){
sup_id = v[0];
address1 = v[2];
address2 = v[3];
city = v[4];
state = v[5];
country = v[6];
zip = v[7];
((*it).loc).push_back(*this);
((*it).num)++;
}
The problem is that a set is sorted. If you'd be allowed to change an element through the iterator, it would basically mean that you could potentially invalidate the iterator and therefore since C++11, the iterator of a set is a constant bidirectional iterator and thereby has the same semantics as the set's const_iterator.
The solution, although slightly ugly, is to remove, modify and re-insert the element. There is a proposal to allow modification of the keys of associative containers, but I don't know if there is any progress in getting it standardized.

Move constructor not getting called? (C++11)

In the following example, why doesn't the move constructor get called in the construction of 'copy' inside fun, even though the 'src' argument of 'fun' is explicitly a rvalue reference and is only used in that construction?
struct Toy {
int data;
Toy(): data(0)
{
log("Constructed");
}
Toy(Toy const& src): data(src.data)
{
log("Copy-constructed");
}
Toy(Toy&& src): data(src.data)
{
log("Move-constructed");
}
};
Toy fun(Toy&& src)
{
Toy copy(src);
copy.data = 777;
return copy;
}
Toy toy(fun(Toy())); // LOG: Constructed Copy-constructed
While Bob && b is an rvalue reference, all named use of data after construction is using it as an lvalue.
So Bob&& b will only bind to rvalues, but when you use it it will not move.
The only ways to get an rvalue reference are:
A value without a name, such as a temporary return value or result of a cast.
Use of a local value variable in a simple return x; statement.
Explicitly casting to an rvalue, such as with std::move or std::forward.
This prevents data from being silently moved from on one line and then used on the next. It can help to think of rvalue as being 'I the programmer say this is not needed after this expression' at use, and 'only take things that are not needed afterwards' in function parameters. The temporary/return exceptions above are two spots the compiler can relatively safely guarantee this itself.
Finally, note that universal references (auto&& and T&&) look like rvalue references but sometimes are not.

Fuzzy/approximate checking of solutions from algorithms

We have people who run code for simulations, testing etc. on some supercomputers that we have. What would be nice is, if as part of a build process we can check that not only that the code compiles but that the ouput matches some pattern which will indicate we are getting meaningful results.
i.e. the researcher may know that the value of x must be within some bounds. If not, then a logical error has been made in the code (assuming it compiles and their is no compile time error).
Are there any pre-written packages for this kind of thing. The code is written in FORTRAN, C, C++ etc.
Any specific or general advice would be appreciated.
I expect most unit testing frameworks could do this; supply a toy test data set and see that the answer is sane in various different ways.
A good way to ensure that the resulting value of any computation (whether final or intermediate) meets certain constraints, is to use an object oriented programming language like C++, and define data-types that internally enforce the conditions that you are checking for. You can then use those data-types as the return value of any computation to ensure that said conditions are met for the value returned.
Let's look at a simple example. Assume that you have a member function inside of an Airplane class as a part of a flight control system that estimates the mass of the airplane instance as a function of the number passengers and the amount of fuel that plane has at that moment. One way to declare the Airplane class and an airplaneMass() member function is the following:
class Airplane {
public:
...
int airplaneMass() const; // note the plain int return type
...
private:
...
};
However, a better way to implement the above, would be to define a type AirplaneMass that can be used as the function's return type instead of int. AirplaneMass can internally ensure (in it's constructor and any overloaded operators) that the value it encapsulates meets certain constraints. An example implementation of the AirplaneMass datatype could be the following:
class AirplaneMass {
public:
// AirplaneMass constructor
AirplaneMass(int m) {
if (m < MIN || m > MAX) {
// throw exception or log constraint violation
}
// if the value of m meets the constraints,
// assign it to the internal value.
mass_ = m;
}
...
/* range checking should also be done in the implementation
of overloaded operators. For instance, you may want to
make sure that the resultant of the ++ operation for
any instance of AirplaneMass also lies within the
specified constraints. */
private:
int mass_;
};
Thereafter, you can redeclare class Airplane and its airplaneMass() member function as follows:
class Airplane {
public:
...
AirplaneMass airplaneMass() const;
// note the more specific AirplaneMass return type
...
private:
...
};
The above will ensure that the value returned by airplaneMass() is between MIN and MAX. Otherwise, an exception will be thrown, or the error condition will be logged.
I had to do that for conversions this month. I don't know if that might help you, but it appeared quite simple a solution to me.
First, I defined a tolerance level. (Java-ish example code...)
private static final double TOLERANCE = 0.000000000001D;
Then I defined a new "areEqual" method which checks if the difference between both values is lower than the tolerance level or not.
private static boolean areEqual(double a, double b) {
return (abs(a - b) < TOLERANCE);
}
If I get a false somewhere, it means the check has probably failed. I can adjust the tolerance to see if it's just a precision problem or really a bad result. Works quite well in my situation.

Resources