Why can't I overload operator=? - c++

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;
}
};

Related

STL way of creating/filling std::set from std::vector

I want to create and fill a set from the contents of a member variable of each entry of a vector. This is what I am doing:
struct S { int i; };
int main()
{
std::vector<S*> structPtrs;
// code to fill the above vector
// create set from the above vector
std::set<int> setInts;
for (auto it = structPtrs.begin(); it != structPtrs.end(); ++it)
{
setInts.insert((*it)->i);
}
}
Is there an STL way to do it? Or via any available method(s) in <algorithm>?
You can always apply std::transform from the range defined by the vector onto the "range" defined by an std::inserter:
transform(begin(structPtrs), end(structPtrs),
inserter(setInts, end(setInts)), [] (S* s) {
return s->i;
});
That should be more than enough use of the standard library.
If you are willing to look beyond the standard library, there is also the option of using something like boost::transform_iterator, which will allow you to move the range transformation into the set's initialization:
auto transfomer = [](S* s) { return s->i; };
std::set<int> setInts(
boost::make_transform_iterator(begin(structPtrs), transfomer),
boost::make_transform_iterator(end(structPtrs), transfomer)
);
You could use std::transform with an appropriate lambda and an insert iterator:
std::transform(structPtrs.begin(), structPtrs.end(), std::inserter(setInts, setInts.end()),
[](S* sp) { return sp->i; });
But personally, I find a simple range for loop to be much easier to follow:
for (S* sp : structPtrs)
setInts.insert(sp->i);
There is one other way you can do this. If you add conversion operator to int to your struct you can just use range constructor directly
#include <iostream>
#include <set>
#include <vector>
using namespace std;
struct test {int i; operator int() {return i;}};
int main() {
vector<test> v;
v.push_back(test{433});
v.push_back(test{533});
set<int> s(v.begin(), v.end());
cout << *(++s.begin());
return 0;
}
https://www.ideone.com/qJwtwc

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;
}

automatic conversion between stl vectors

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 ^^

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;
}

Functor's instance variable is not kept between consecutive calls to generate_n

I am using the following struct as an input to STL's generate_n algorithm:
struct GenerateNumber {
GenerateNumber () : i(0) {}
int operator () (void) {
return i++;
}
private:
int i;
};
An example to the code that uses this functor is:
std::vector <int> v1 (3);
std::vector <int> v2 (3);
GenerateNumber generateNumber;
std::generate_n (v1.begin (), 3, generateNumber);
std::generate_n (v2.begin (), 3, generateNumber);
However, the result is that both v1 and v2 contain {0,1,2}, instead of v2 to contain {3,4,5}. I have verified with a breakpoint that the GenerateNumber's constructor is called only once (I know it doesn't make sense that the constructor is called more then once, but I checked it anyway).
I know I can solve this by making i static, but I don't understand this behavior. How come the value of i is not kept between consecutive calls?
The generator object is copied when passed to generate_n. Try using std::ref, i.e.
std::generate_n(v1.begin(), 3, std::ref(generateNumber));
std::generate_n(v2.begin(), 3, std::ref(generateNumber));
Edit: Note that std::ref is only available in C++11. It was introduced in TR1 as std::tr1::ref, and is also available in boost as boost::ref.
std::generate_n takes the functor by value, that is, it makes a copy of it. It could be that you didn't check that the copy constructor was being called.
In the absence of std::ref, and if your problem is localised as in your example, you could modify your functor to take a reference to a counter set in the scope of the calls to std::generate_n:
struct GenerateNumber {
GenerateNumber (int& i) : struct GenerateNumber {
GenerateNumber () : i(0) {}
int operator () (void) {
return i++;
}
private:
int& i;
};
int main() {
int counter = 0;
std::vector <int> v1 (3);
std::vector <int> v2 (3);
GenerateNumber generateNumber(counter);
std::generate_n (v1.begin (), 3, generateNumber);
std::generate_n (v2.begin (), 3, generateNumber);
}