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;
}
Related
I have a class like this:
class MyClass {
MyClass(double *v, int size_of_v){
/*do something with v*/
};
};
My question: Is there any way, I can initialize such class without defining an array of double and feeding it to the constructor?
I would like to do something like:
auto x = MyClass({1.,2.,3.}, 3);
It is called list initialization and you need a std::initilizer_list constructor, that to be achieved in your MyClass.
#include <initializer_list>
class MyClass
{
double *_v;
std::size_t _size;
public:
MyClass(std::initializer_list<double> list)
:_v(nullptr), _size(list.size())
{
_v = new double[_size];
std::size_t index = 0;
for (const double element : list)
{
_v[index++] = element;
}
};
~MyClass() { delete _v; } // never forget, what you created using `new`
};
int main()
{
auto x = MyClass({ 1.,2.,3. }); // now you can
//or
MyClass x2{ 1.,2.,3. };
//or
MyClass x3 = { 1.,2.,3. };
}
Also note that providing size_of_v in a constructor is redundant, as it can be acquired from std::initializer_list::size method.
And to completeness, follow rule of three/five/zero.
As an alternative, if you can use std::vector, this could be done in a much simpler way, in which no manual memory management would be required. Moreover, you can achieve the goal by less code and, no more redundant _size member.
#include <vector>
#include <initializer_list>
class MyClass {
std::vector<double> _v;
public:
MyClass(std::initializer_list<double> vec): _v(vec) {};
};
Well you can use std::vector instead the double*v & it would fit perfectly for your goal
class MyClass {
MyClass(vector<double> v){
/*do something with v*/
};
};
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 ^^
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;
}
Let's assume I have a non-STL vector type that is compatible with std::vector by an operator std::vector<T>. Is it possible to move its elements to a std::vector instead of the default copy construction, so that
OtherVectorType<SomeClass> f()
{
OtherVectorType<SomeClass> v;
v.pushBack(SomeClass());
v.pushBack(SomeClass());
v.pushBack(SomeClass());
return v;
}
std::vector<SomeClass> sv = f();
would use SomeClass's move constructor (3 times) when creating the std::vector sv?
I imagine something like
template<typename T>
std::vector<T>& operator= (std::vector<T>& self, OtherVectorType<T>&& from)
{
[...]
}
but haven't found any working solution yet.
For illustration, this is how the std::vector operator is defined:
template<typename T> class OtherVectorType
{
[...]
operator std::vector<T>() const
{
if (!m_size)
return std::vector<T>();
return std::vector<T>(reinterpret_cast<T*>(m_pElements),
reinterpret_cast<T*>(m_pElements) + m_size);
}
}
I think you need support for rvalue references for *this.
operator std::vector<T>() const &; // copy your own type's data
operator std::vector<T>() &&; // move it into the std::vector<T>
Sadly, support is rare, even GCC 4.8 does not have it. :(
The easiest thing to do (especially if you don't have rvalue-this) is to make use of make_move_iterator as demonstrated below:
#include <deque>
#include <vector>
#include <memory>
#include <iterator>
typedef std::unique_ptr<int> SomeClass;
typedef std::deque<SomeClass> OtherVectorType;
OtherVectorType
f()
{
OtherVectorType v;
v.push_back(SomeClass(new int (1)));
v.push_back(SomeClass(new int (2)));
v.push_back(SomeClass(new int (3)));
return v;
}
std::vector<SomeClass>
to_vector(OtherVectorType&& o)
{
return std::vector<SomeClass>(std::make_move_iterator(o.begin()),
std::make_move_iterator(o.end()));
}
int main()
{
std::vector<SomeClass> v = to_vector(f());
}
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;
}
};