automatic conversion between stl vectors - c++

What I want is to copy an std::vector<int> to another std::vector<myStruct> with assignment operator in which myStruct can be assigned an int. So I wrote this piece of code:
#include <vector>
#include <iostream>
using namespace std;
struct myStruct
{
myStruct(int& a) : _val(a) { }
myStruct(int&& a) : _val(a) { }
myStruct& operator=(int& a)
{
_val = a;
return *this;
}
int _val;
};
int main()
{
vector<int> ivec;
ivec.push_back(1);
vector<myStruct> svec = ivec;
return 0;
}
And it gives me error as it cannot find a valid conversion between std::vector<myStruct> and std::vector<int> although int can implicitly be converted to myStruct. On the other hand, assign operator cannot be declared outside the class so I deduce writing an operator manually is not an option. So what should I do in this situation?
*** UPDATE:
As Blastfurnace and others said this can be solved using this code instead of assignment:
vector<myStruct> svec(ivec.begin(), ivec.end());
But imagine the situation in which I want to write a library and want to handle this in the library itself so the user can just write std::vector<myStruct> svec = someFunction() in which someFunction returns std::vector<int>. Isn't there any solution for this?

You could use the constructor overload that takes an iterator range:
vector<myStruct> svec(ivec.begin(), ivec.end());

You can use vector<myStruct> svec(ivec.begin(), ivec.end()); instead.
You could also use std::copy(ivec.begin(), ivec.end(), std::back_inserter(svec)); or svec.assign(ivec.begin(), ivec.end()); as it might be better in case you assign multiple times as it can reuse the capacity of the vector<myStruct> after it was cleared.

Your best bet is having a conversion function
std::vector< myStruct> convertVector( const std::vector< int> & other)
{
return std::vector< myStruct> ( ivec.begin(), ivec.end() );
}
that would automatically use return value optimization so no overhead for copying the data twice (you just copy data once instead when you iterate the "other" vector).
Just for the sake of completeness:
By using custom datatypes it is possible adding custom type conversion but I guess that's generally a bad Idea and you may not be interested in, anyway:
class IntVector: public std::vector<int>{
public:
//conversion operator (note a VERY BAD IDEA using this, may come in handy in few cases)
operator myStructVector () {
return convertVector(*this);
}
}
class myStructVector: public std::vector< myStruct>{
//....
};
usage:
int main()
{
IntVector ivec;
ivec.push_back(1);
myStructVector svec = (myStructVector)ivec;
return 0;
}
I would discourage going this way anyway ^^

Related

transform vector of shared_ptr with non const elements to vector of shared_ptr with const elements

I have a class A containing a vector of shared_ptr<B>.
I implemented a getter to this vector.
In some cases, it would be nice to ensure that the content in B does not change (make B read only or a const reference).
If I would not have used vector<shared_ptr<B>> but rather vector<B> I could simply write two getters, one returning a const reference (read only), and one returning a reference only (manipulation possible). #
Is there a way to do the same thing with a vector<shared_ptr<B>>?
Maybe it is easier to understand the problem in this code:
#include <vector>
#include <memory>
using namespace std;
class B{
public:
explicit B(int i) : i_{i} {}
void set_i(int i){i_ = i;}
private:
int i_ = 0;
};
class A{
public:
const vector<shared_ptr<B>> &get_vb(){return vb;}
// const vector<shared_ptr<const B>> &get_vb_const(){return vb;} // I would like to return a const vector with const elements in some cases
private:
vector<shared_ptr<B>> vb{make_shared<B>(1), make_shared<B>(10), make_shared<B>(100)};
};
int main() {
A a;
const auto &vb = a.get_vb();
vb[0]->set_i(2);
// const auto &vb_const = a.get_vb_const(); // somehow I would like to gain this vector without being able to modify the elements
// vb_const[0]->set_i(2); // should throw error
return 0;
}
You need to construct a new vector with the desired elements:
const vector<shared_ptr<const B>> get_vb_const() const {
return vector<shared_ptr<const B> > {vb.cbegin(), vb.cend()};
}
Note that the function doesn't return a reference now because we are creating a temporary and returning it.
transform vector of shared_ptr with non const elements to vector of shared_ptr with const elements
You can use the constructor of vector that accepts a pair of iterators to perform the conversion.
You can avoid the overhead of allocating and copying a vector by implementing a custom const iterator for your class.

How to write a getter for a container of shared_ptrs which does not allow modification of data

Let's say I have a private variable which is a vector of shared_ptrs to non-const objects.
Is it possible to write a getter method which only allows read access to the data pointed to by the shared pointers?
I want to be able to use range-based loops for elegance, so I want to avoid writing const_iterators.
My understanding is that const shared_ptr<T> makes the pointer itself const, not T. I tried to compile shared_ptr<const T>, but it doesn't compile if T itself is not declared const in the class.
In other words, how could I write something like:
#include <iostream>
#include <vector>
#include <memory>
using std::vector;
using std::shared_ptr;
using std::make_shared;
using std::cout;
using std::endl;
class MyClass{
public:
MyClass(int element1, int element2)
{
myVector_.push_back(std::make_shared<int>(element1));
myVector_.push_back(std::make_shared<int>(element2));
}
// I want something like this, but doesn't compile
// const vector<shared_ptr<const int>> readMyVector() const {return myVector_;}
const vector<shared_ptr<int>> readMyVector() const {return myVector_;}
private:
// Should NOT be <const int>, the class should be able to modify its elements
vector<shared_ptr<int>> myVector_;
};
int main(){
auto testobject = MyClass(1,2);
for (auto my_protected_data : testobject.readMyVector()){
cout<<(*my_protected_data)<<endl;
(*my_protected_data) = 25;
cout<<(*my_protected_data)<<endl; // Should not happen
}
return 0;
}
The correct type to return is std::vector<std::shared_ptr<const int>>, but you'll have to make that vector by hand. std::shared_ptr<T> is convertible to std::shared_ptr<const T>, but the problem is that std::vector<T> isn't implicitly convertible to std::vector<U> simply because T is convertible to U.
The easiest way is to construct a vector from your internal vector's begin and end iterators.
vector<shared_ptr<const int>> readMyVector() const
{
return{ myVector_.begin(), myVector_.end() };
}
Note that adding const to the return type of a function that returns by value is rarely useful.
You should also ask yourself rather it's worth it to copy all of those std::shared_ptr. You may want to consider simply returning a vector of int.
If you want to make your getter to return vector of shared pointers to const data, there is only one way, to return copy of shared pointers to const data.
const vector<shared_ptr<const int>> readMyVector() const
{
vector<shared_ptr<const int>> cdata(myVector_.begin(), myVector_.end());
return cdata;
}

C++ - create new constructor for std::vector<double>?

I have written a custom container class which contains a std::vector<double> instance - works nicely. For compatibility with other API's I would like to export the content of the container as a std::vector<double> copy . Currently this works:
MyContainer container;
....
std::vector<double> vc(container.begin(), container.end());
But if possible would like to be able to write:
MyContainer container;
....
std::vector<double> vc(container);
Can I (easily) create such a std::vector<double> constructor?
You can create an explicit conversion to std::vector<double>:
explicit operator std::vector<double>() const {
return std::vector<double>(begin(), end());
}
Then, std::vector<double> vc(container); will invoke the std::vector<double> move constructor.
Note that conversions that are computationally expensive are generally frowned upon. Therefore, a vector factory function may be a wiser approach:
class MyContainer {
public:
using value_type = double;
// ...
};
template<typename Source>
auto to_vector(Source source) {
return std::vector<typename Source::value_type>(source.begin(), source.end());
}
Then you'd write:
MyContainer container;
// ...
auto vc = to_vector(container);
This is also more generic as it works with anything that has compatible value_type, begin and end members.
Can I (easily) create such a std::vector constructor?
No you can't, since this would require to change the std::vector class declarations.
You can provide a cast operator for MyContainer to std::vector<double> though.
You cannot, and should not, change the API of a class you didn't write yourself. But I think in your case a cast operator would do just fine. For example (this one needs -std=c++11):
#include <iostream>
#include <vector>
struct Foo
{
operator std::vector<double> () const
{
return std::vector<double> { 1, 2, 3 };
}
};
int main()
{
Foo foo;
std::vector<double> bar = foo; // Applies the cast operator defined in Foo
std::cout << bar.size() << std::endl; // Prints "3"
return 0;
}

Populate vector with integer sequence, on construction

I am trying to populate a vector (or other container) with a sequence of integers on construction of the vector (contrasting to this question). The following code does what I intend, using the range constructor for a vector, and Boost's counting_range:
#include <iostream>
#include <vector>
#include <boost/range/counting_range.hpp>
using namespace std;
int main () {
vector<int> test_vector(boost::counting_range(2,10).begin(),boost::counting_range(2,10).end());
for (auto i : test_vector) cout << i << endl;
}
Questions:
Can I eliminate the duplication in counting_range(2,10).begin() and counting_range(2,10).end()? Currently I'm specifying the range of (2,10) twice.
Can this be done without Boost, using just vanilla C++11 or C++0x? Edit: or C++14?
Edit:
I'd like to instantiate the vector and specify the range all in a single statement. For example in Python I could write test_vector=range(2,9). In R/Octave/Matlab one can write test_vector=2:9 or test_vector=seq(2,9,1). In this regard I'm satisfied with what I have above.
I used "2" and "10" above, but boundary of the range could be dynamic, any integers a,b in scope with a≤b. So an initialization list like {2,3,...,9} isn't desirable since it must be specified at compile-time.
The method I've used above can also be used to initialize other containers in a single statement; the following also works:
unordered_set test_set(boost::counting_range(2,10).begin(),boost::counting_range(2,10).end());
It would be nice if any solution was also as 'container independent'.
I agree with #david-rodriguez-dribeas that we can adhere to Meyer's Item by initializing the vector in the constructor's body; it isn't necessary for it to be done in the constructor's initialization list. So I have struck-out this portion below.
Motivation:
I want to do this is because I want to obey Meyer's Item 4 ("Make sure objects are initialized before they are used.") in Effective C++ elsewhere in my code. For example:
class my_class {
public:
my_class()
:vec(boost::counting_range(2,10).begin(),boost::counting_range(2,10).end()) {}
vector<int> vec;
};
Don't over do it. The simple thing to do is to default initialize the vector, reserve and initialize from the range.
Make sure objects are initialized before they are used.
That does not mean that the member must be fully initialized in the initialization list, rather than the my_class object must be fully initialized when the constructor completes.
Other than that, just for the sake of it, there are different things you can do in vanilla C++ to handle this, like creating a helper function and returning the vector by value:
std::vector<int> create_vector() {
std::vector<int> v;
// ...
return v;
}
But I would not use this (or any other alternative) to initialize a member, only if needed (the vector is const might be sufficient excuse :))
You can constructor std::vector<T> with a sequence delimited by ranges. You just need a suitable input iterator to initialize the sequence and you can define your iterator such that you can use a default constructed iterator for the end, e.g.:
class counter: public std::iterator<std::input_iterator_tag, int> {
int current;
int end;
public:
counter(): current(), end() {}
counter(int c, int e): current(c), end(e) {}
int const& operator*() const { return this->current; }
counter& operator++() { ++current; return *this; }
counter operator++(int) { counter rc(*this); ++current; return rc; }
bool operator== (counter const& other) const {
return (end - current) == (other.end - other.current);
}
bool operator!= (counter const& other) const { return !(*this == other); }
};
std::vector<int> v(counter(2, 10), counter());
The Boost way to do this is to use boost::copy_range:
auto vec = boost::copy_range<std::vector<int>>(boost::irange(0, 10));
boost::copy_range is a function template that returns an object of its first template parameter type:
template< typename SeqT, typename Range >
inline SeqT copy_range( const Range& r )
{
return SeqT( boost::begin( r ), boost::end( r ) );
}
You do have to specify the return type as a template parameter, which can violate DRY but can be avoided using AAA style, as in my example above, or using decltype in a class initializer:
class S {
public:
S() : vec(boost::copy_range<decltype(vec)>(boost::irange(0, 10)) {}
private:
const std::vector<int> vec;
};
This has never failed anyone, and it's completely vanilla, even works with C++03 (provided you backport begin, end, like I did; if not, just use compile-time array size):
int inits[] = { 2, 3, 4, 5, 6, 7, 8, 9, 10};
vector<int> test_vector ( begin(inits), end(inits) );
If the sequence is another kind of sequence, you only need to adapt your initialization array for it, or use a generator object, and bam! It's done.
As for avoid the duplication in (1), what's wrong if anything with the following?
const boost::counting_range init_range(2,10);
vector<int> test_vector( begin(init_range), end(init_range) );
Instead of creating boost::counting_range, you can use directly boost::counting_iterator to initialize your sequence.
#include <iostream>
#include <vector>
#include <boost/iterator/counting_iterator.hpp>
int main ()
{
std::vector<int> aVec(boost::counting_iterator<int>(0), boost::counting_iterator<int>(10));
for (int i : aVec)
{
std::cout << i << std::endl;
}
return 0;
}

Why can't I overload operator=?

I want to make following code work:
Mylist lst;
vector<int> v = lst;
So I see I need to convert my list into a vector.
I tried this code:
vector<int> operator=(vector<int> v, const List & l) {
return v; // more profound stuff later :-)
}
(placed it outside the class). unfortunately Visual Studio throws me: "Error: 'operator=' must be a member function".
And I don't get it - what should I do? I can't place this function inside vector class...
Can you help me out with that?
Thanks!
You can overload the cast of your type to any other type (see this and other resources easily discoverable by Google). I think this is what you need here. Overloading the assignment operator is used for quite another thing in C++.
Mylist lst;
vector<int> v = lst;
This doesn't call operator= to begin with. It attempt to call a constructor of vector which takes argument of type Mylist. Since no such constructor exists, it gives compilation error.
As for the question in the title, you can definitely overload operator=, but it cannot be a free-function. It has to be a member of a class.
You can't overload operator for certain class outside this class. I gues vector is std::vector. What you could do is add to your "Mylist" class method toStdVector() const that would return std::vector.
For example:
class Mylist {
public:
std::vector<int> toStdVector() const { ... }
};
Mylist list;
std::vector<int> v(list.toStdVector());
To make
MyList lst;
std::vector v = lst;
To make the above work you'll need to overload operator std::vector<int> () (or any other variable type that is compatible with std::vector<int>) in MyList, see the following example.
Another way of doing it is to provide a method that you explicitly call to return a std::vector<int> "version" of your object. std::vector<int> v = obj.to_int_vector (), it's pretty much the same implementation as in the below example.
Example
#include <iostream>
#include <vector>
#include <iterator>
#include <algorithm>
struct Obj {
operator std::vector<int> () const {
return std::vector<int> (data, data+3);
}
int data[3];
};
int
main (int argc, char *argv[])
{
Obj o;
o.data[0] = -1;
o.data[1] = 2;
o.data[2] = 99;
std::vector<int> v = o;
std::copy (v.begin (), v.end (), std::ostream_iterator<int> (std::cout, " "));
}
output:
-1 2 99
You may provide operator vector<int> So if you put vector<int> v = lst;, the operator vector` would be called and operator= for vector will be allowed.
class MyList {
public:
operator vector<int> () const
{
return some_valid_vector;
}
};