C++ STL question related to insert iterators and overloaded operators - c++

#include <list>
#include <set>
#include <iterator>
#include <algorithm>
using namespace std;
class MyContainer {
public:
string value;
MyContainer& operator=(const string& s) {
this->value = s;
return *this;
}
};
int main()
{
list<string> strings;
strings.push_back("0");
strings.push_back("1");
strings.push_back("2");
set<MyContainer> containers;
copy(strings.begin(), strings.end(), inserter(containers, containers.end()));
}
The preceeding code does not compile. In standard C++ fashion the error output is verbose and difficult to understand. The key part seems to be this...
/usr/include/c++/4.4/bits/stl_algobase.h:313: error: no match for ‘operator=’ in ‘__result.std::insert_iterator::operator* [with _Container = std::set, std::allocator >]() = __first.std::_List_iterator::operator* [with _Tp = std::basic_string, std::allocator >]()’
...which I interpet to mean that the assignment operator needed is not defined. I took a look at the source code for insert_iterator and noted that it has overloaded the assignment operator. The copy algorithm must uses the insert iterators overloaded assignment operator to do its work(?).
I guess that because my input iterator is on a container of strings and my output iterator is on a container of MyContainers that the overloaded insert_iterator assignment operator can no longer work.
This is my best guess, but I am probably wrong.
So, why exactly does this not work and how can I accomplish what I am trying to do?

What would work would be using the constructor (which would make more sense instead of the assignment):
class MyContainer {
public:
string value;
MyContainer(const string& s): value(s) {
}
};
Then the second problem is that set also requires its contents to be comparable.
As to the cause, insert_iterator works by overloading operator=:
insert_iterator<Container>& operator= (typename Container::const_reference value);
As you can see, the righthand value must be either the value type of the container or implicitly convertible to it, which is exactly what a (non-explicit) constructor achieves and the assignment operator doesn't.
Technically you could also make it work without changing the class (e.g if you don't want an non-explicit constructor) by providing a suitable conversion function:
MyContainer from_string(const std::string& s)
{
MyContainer m;
m = s; //or any other method how to turn a string into MyContainer
return m;
}
which can be used with std::transform:
transform(strings.begin(), strings.end(), inserter(containers, containers.end()), from_string);

You need to add:
1. Constructor that takes string (you are trying to add string to container that can contain MyContainer objects).
2. bool operator < (set uses it by default to compare elements)
For instance :
class MyContainer
{
public:
MyContainer(const string& v):value(v){};
};
bool operator <(const MyContainer &c1, const MyContainer &c2)
{
return c1.value <c2.value;
}

The problem is twofold:
You're trying to fill a set of MyContainer objects
... from a list of string objects.
The copy() algorithm tries to convert each string object to a MyContainer object. In C++ to add to class MyContainer conversion support from type string to type MyContainer you need to add a constructor that takes a parameter of type string:
struct MyContainer {
MyContainer(const string& s) : value(s) { }
bool operator<(const MyContainer& o) const { return value < o.value; }
private:
string s;
};
You don't need an assignment operator, because the compiler can get the copying done by the copy-constructor: convert a string to a MyContainer and then use the default assignment operator to assign one MyContainer object onto the other. You will, however need an operator<() because C++ sets are sorted.

Related

find by hash value from an unordered_set which element is cumtomed type

For example I have a class like this:
struct Foo {
std::string unique_name;
unsigned id;
struct HashFunc {
size_t operator()(const Foo &foo) const {
return std::hash<std::string>()(foo.unique_name);
}
};
struct KeyEqual {
bool operator()(const Foo &lhs, const Foo &rhs) const {
return lhs.unique_name == rhs.unique_name;
}
};
};
as you can see I want the field unique_name to the key for hashing.
then I put some Foos in an unordered_set:
std::unordered_set<Foo, Foo::HashFunc, Foo::KeyEqual> my_set {{"Foo", 1}, {"Bar", 2}};
Now I'd like to find the element which unique_name is Bar, but I don't know how to write, the std::set::find function takes type Foo as argument, not std::string.
So how can I write something like my_set.find("Bar")?
In order to do something like my_set.find("Bar") you need to do two things:
Your C++ compiler must support the C++20 standard and you must enable C++20 when compiling your code.
Implement your comparison and hash classes to be "transparent". This boils down to implementing overloads that hash a std::string (or a const char *) in addition to your class, and implementing a comparison operator between your class and a string.
For more information see the reference for std::unordered_set::find.
Before C++20, your only option is to construct a temporary instance of your class and pass it to find instead of a string (this can be done by implementing an appropriate constructor and relying on implicit conversions).

How to define an implicit conversion for typedefs?

I want to have an automatic conversion from std::string into my type my_type, defined as
typedef std::pair<std::string,int> my_type;
such that the .first part of the converted my_type is the string and the .second part is always 0.
It should also work if one calls the function std::string fun(my_type x, ...) { return x.first; } with, say,
std::string s = "Hello"; fun(s, ...);.
I don't want to define a new class instead of my_type and also not to overload all of my functions if possible. I tried to wrap my head around how to use operator for this but I can't get my program to compile.
EDIT:
Since this doesn't seem to be possible without defining a custom struct or so, here is a workaround that I came up with, but I was hoping it can be achieved without defining a new class/struct. Thank you for saving me more time trying to do this, though.
class Element {
public:
Element() {};
Element(std::string s, int a) { name = s; value = a; };
Element(std::string s) { name = s; value = 0; };
...
std::string return_name() { return name; };
private:
std::string name;
int value;
};
std::string fun(Element x) { return x.return_name(); };
Calling std::string s = "Hello"; fun(s); works now automatically.
It is not possible to add new implicit conversions for existing classes, such as std::pair. Implicit conversions can only be member functions:
A non-explicit constructor that can be called with one argument. If there are more arguments, they must have default values.
operator T() const conversion operator.
And it is not possible to add new member functions to classes without changing class definition. This restriction is in place to prevent a function introduced at global or namespace scope from changing the semantics of your existing code.
What you can do instead is create a new class with a conversion constructor (a non-explicit constructor that can be called with one argument):
struct MyPair : std::pair<std::string, int> {
// In this class scope pair now refers to std::pair<std::string, int>.
MyPair(std::string const& a)
: pair(a, 0)
{}
MyPair(pair const& a)
: pair(a)
{}
};
The derivation from std::pair<std::string, int> makes it possible to pass MyPair where std::pair<std::string, int> is expected. And another constructor for converting std::pair<std::string, int> to MyPair.

No viable '=' when using vector::erase

I am having some trouble with figuring out why I cannot seem to get std::vector::erase work with a vector of my own class objects. The following code throws a "No viable overloaded '='" error, and cannot figure out why after some extensive searching overflow/tutorialspoint/...
my class definition 'MyClass.hpp':
#include <string>
#include <vector>
class node;
class graph{
public:
graph();
std::vector<node> nodeList;
};
class node{
public:
node(std::string name) : name(name){};
void operator=(node& rhs);
std::string name;
std::vector<node> adjacent;
};
void node::operator=(node& rhs){
name = rhs.name;
adjacent = rhs.adjacent;
}
and my main file:
#include "MyClass.hpp"
int main(int argc, const char * argv[]) {
node node1("root"), node2("leaf");
node1.adjacent.push_back(node2);
node2.adjacent.push_back(node1);
node1.adjacent.erase(node1.adjacent.begin());
graph myGraph;
return 0;
}
std::vector::erase adds the requirement that the type T of the vector's elements must be move assignable. This means in particular that an expression like t = rv must have a return type of T & (reference to T) and a value of t (so the returned reference must reference the assigned to object).
Your Node::operator= has return type void, thus violates above requirement. Further, the usual (and probably most reasonable) type signatures for copy / move assignment operators are as follows:
T & operator=(T &&); // move assignment
T & operator=(T); // copy assignment, pass by value
T & operator=(T const &); // copy assignment, pass by const reference
But, instead of fixing your operator, you should discard it all together! Your Node class does (should) not deal with ownership (the std::vector is doing this for you), thus per the rule of zero you should not provide a custom copy or move assignment operators (nor a destructor).

How to create a set with my customized comparison in c++

Could someone explain me what is going on in this example here?
They declare the following:
bool fncomp (int lhs, int rhs) {return lhs<rhs;}
And then use as:
bool(*fn_pt)(int,int) = fncomp;
std::set<int,bool(*)(int,int)> sixth (fn_pt)
While the example for the sort method in algorithm library here
can do like this:
bool myfunction (int i,int j) { return (i<j); }
std::sort (myvector.begin()+4, myvector.end(), myfunction);
I also didn't understand the following:
struct classcomp {
bool operator() (const int& lhs, const int& rhs) const
{return lhs<rhs;}
};
this keyword operator (not being followed by an operator as in a op. overload)... what is the meaning of it? Any operator applied there will have that behavior? And this const modifier... what is the effect caused by it?
I was trying to make a set of C-style string as follows:
typedef struct
{
char grid[7];
} wrap;
bool compare(wrap w1, wrap w2)
{
return strcmp(w1.grid, w2.grid) == -1;
}
set <wrap, compare> myset;
I thought I could create a set defining my sorting function in a similar as when I call sort from algorithm library... once it didn't compile I went to the documentation and saw this syntax that got me confused... Do I need to declare a pointer to a function as in the first example i pasted here?
struct classcomp {
bool operator() (const int& lhs, const int& rhs) const
{return lhs<rhs;}
};
Defines a functor by overloading the function call operator. To use a function you can do:
int main() {
std::set <wrap, bool (*)(wrap,wrap)> myset(compare);
return 0;
}
Another alternative is to define the operator as a part of the wrap class:
struct wrap {
char grid[7];
bool operator<(const wrap& rhs) const {
return strcmp(this->grid, rhs.grid) == -1;
}
};
int main() {
wrap a;
std::set <wrap> myset;
myset.insert(a);
return 0;
}
You're almost there... here's a "fixed" version of your code (see it run here at ideone.com):
#include <iostream>
#include <set>
#include <cstring>
using namespace std;
typedef struct
{
char grid[7];
} wrap;
bool compare(wrap w1, wrap w2) // more efficient: ...(const wrap& e1, const wrap# w2)
{
return strcmp(w1.grid, w2.grid) < 0;
}
set <wrap, bool(*)(wrap, wrap)> myset(compare);
int main() {
wrap w1 { "abcdef" };
wrap w2 { "ABCDEF" };
myset.insert(w1);
myset.insert(w2);
std::cout << myset.begin()->grid[0] << '\n';
}
"explain [to] me what is going on in this example"
Well, the crucial line is...
std::set<wrap, bool(*)(wrap, wrap)> myset(compare);
...which uses the second template parameter to specify the type of function that will perform comparisons, then uses the constructor argument to specify the function. The set object will store a pointer to the function, and invoke it when it needs to compare elements.
"the example for the sort method in algorithm library..."
std::sort in algorithm is great for e.g. vectors, which aren't automatically sorted as elements are inserted but can be sorted at any time. std::set though needs to maintain sorted order constantly, as the logic for inserting new elements, finding and erasing existing ones etc. all assumes the existing elements are always sorted. Consequently, you can't apply std::sort() to an existing std::set.
"this keyword operator (not being followed by an operator as in a op. overload)... what is the meaning of it? Any operator applied there will have that behavior? And this const modifier... what is the effect caused by it?
operator()(...) can be invoked on the object using the same notation used to call a function, e.g.:
classcomp my_classcomp;
if (my_classcomp(my_int1, my_int_2))
std::cout << "<\n";
As you can see, my_classcomp is "called" as if it were a function. The const modifier means that the code above works even if my_classcomp is defined as a const classcomp, because the comparison function does not need to modify any member variables of the classcomp object (if there were any data members).
You almost answered your question:
bool compare(wrap w1, wrap w2)
{
return strcmp(w1.grid, w2.grid) == -1;
}
struct wrap_comparer
{
bool operator()(const wrap& _Left, const wrap& _Right) const
{
return strcmp(_Left.grid, _Right.grid) == -1;
}
};
// declares pointer to function
bool(*fn_pt)(wrap,wrap) = compare;
// uses constructor with function pointer argument
std::set<wrap,bool(*)(wrap,wrap)> new_set(fn_pt);
// uses the function directly
std::set<wrap,bool(*)(wrap,wrap)> new_set2(compare);
// uses comparer
std::set<wrap, wrap_comparer> new_set3;
std::sort can use either a function pointer or a function object (http://www.cplusplus.com/reference/algorithm/sort/), as well as std::set constructor.
const modifier after function signature means that function can't modify object state and so can be called on a const object.

When would I need to implement operator [] ?

given the following template function :
template <class T>
void DoSomething(T &obj1, T &obj2)
{
if(obj1 > obj2)
cout<<"obj1 bigger: "<<obj1;
else if(obj1 == obj2)
cout<<"equal";
else cout<<"obj2 bigger: "<<obj2;
T tmp(3);
T array[2];
array[0]=obj1;
array[1]=obj2;
}
I need to define a class called MyClass (declarations only , i.e. just the .h file) , that would be able to work with that template function .
I defined the next declarations :
class MyClass
{
public:
MyClass(); // default ctor
MyClass(int x); // for ctor with one argument
bool operator ==(const MyClass& myclass) const;
bool operator >(const MyClass& myclass) const;
friend ostream& operator<<(ostream &out,const MyClass& myclass); // output operator
};
What I don't understand is why there is no need to define operator [] for the lines:
array[0]=obj1; array[1]=obj2;
? When would I need to define operator []?
thanks ,Ron
You declared an array for your type:
T array[2];
But your are talking about implementing operator[] for T, which is totally different concept.
If you need
T t;
t[1] = blah
Then you need to implement operator[]
Because
T array[2];
Isn't a T object, its an array of T. So
array[0];
Is indexing an array, not one of your objects, therefore you don't need an operator[].
Assuming you call DoSomething with a couple of MyClass objects, you have declared array to be a normal array of MyClass objects. You did not need a [] operator for MyClass because array is not an instance of MyClass; it is just an array.
You will want to overload the [] operator in your own classes when it makes sense to, or is convenient. A good example is a collection (such as a map). Another example is a custom string class, where you might want to index by a regex object to find matches for your pattern inside your string.
If your class was an implementation of a dynamic array, for example, you would want to access the (single) object as though it was an array - you this by overloading the [] operator.