Inserting objects into vector using lower_bound with compare struct - c++

I have this class:
class Mail {
public:
Mail(const string & msg) : msg(msg) {}
const string msg;
};
And this structure, which compares two Mail objects:
struct Compare {
bool operator()(const Mail & mail, Mail const & mail2) const {
return mail.msg < mail2.msg;
}
};
I want to have a vector with Mail objects sorted by their message const string msg. However, when I try to insert new object into vector using lower_bound, I get many errors, including:
passing ‘const string as ‘this’ argument discards qualifiers.
int main() {
vector <Mail> mails;
Mail mail2("1");
mails.push_back(mail2);
const string msg = "2";
Mail mail(msg);
auto low = lower_bound(mails.begin(), mails.end(), mail, Compare());
// mails.push_back(mail); // OK
mails.insert(low, mail); // passing ‘const string as ‘this’ argument discards qualifiers
return 0;
}
I dont yet understand const usage much and can't figure out, which const is wrong.
I am sorry, if this has already been asked, but I haven't found an answer to this problem yet.

The errors in C++ can sometimes be hard to diagnose. My tip is to always start at the top and resolve that one first. In this case, there's a long list of them, but they're all really about the same thing -- the assignment operator for Mail cannot be generated.
Think of it this way, the compiler is being helpful and is trying to generate (and inside lower_bound(), use) this function:
Mail& operator=( const& Mail mail )
{
msg = mail.msg;
return *this;
}
But it can't because that assignment in the body is invalid due to msg being const. You can't really write it yourself either since you also can't assign to a const variable.
Usually you don't need member variables to be const because they become const if the instance of the class is itself const:
const auto mail1 = Mail{"1"};
auto mail2 = Mail{"2"};
mail1.msg = "3"; // FAIL! msg is const since mail1 is const
mail2.msg = "4"; // Ok! msg is not const
If you do need a const member, you can't use assignment operators with the class. Them's the breaks.
Remove that const and all works:
#include <vector>
#include <string>
#include <algorithm>
using namespace std;
class Mail {
public:
Mail(const string & msg) : msg(msg) {}
string msg; //////////////////////////////// Not const!
};
struct Compare {
bool operator()(const Mail & mail, Mail const & mail2) const {
return mail.msg < mail2.msg;
}
};
int main() {
vector <Mail> mails;
Mail mail2("1");
mails.push_back(mail2);
const string msg = "2";
Mail mail(msg);
auto low = lower_bound(mails.begin(), mails.end(), mail, Compare());
// mails.push_back(mail); // OK
mails.insert(low, mail); // OK!
return 0;
}
See it run live on Coliru.
Footnotes:
You could use a lambda for the comaparator to avoid some boilerplate around the class Compare:
const auto low = lower_bound( begin(mails), end(mails), mail,
[]( const auto& mail1, const auto& mail2 )
{ return mail1.msg < mail2.msg; } );
You can use vector::emplace_back() to construct items in place, avoiding a copy. The following blocks do the same thing in effect, but the second is more efficient:
const auto mail = Mail{"2"};
mails.push_back( mail2 ); // Copies
mails.emplace_back("2"); // Creates it right in the vector
Consider using vector::reserve() if you know how many items you'll put in your vector.

The problems here pertain to the deleted copy assignment operator and the deleted move assignment operator because of the const string msg; member in the Mail class:
Deleted implicitly-declared copy assignment operator
A defaulted copy assignment operator for class T is defined as deleted if any of the following is true:
T has a non-static data member of non-class type (or array thereof) that is const;
Deleted implicitly-declared move assignment operator
The implicitly-declared or defaulted move assignment operator for class T is defined as deleted if any of the following is true:
T has a non-static data member that is const;

Related

C++ - String Array Parameter for SetUnion function

I wrote a function to compute the union of two sets.
I'm running into several compilation errors and I believe that's in part due to how I made the StringUnion array and declared it but nothing I do is working thus far.
This is my header file.
#ifndef StringSet_header
#define StringSet_header
#include <memory>
#include <string>
using std::string;
using std::unique_ptr;
using std::make_unique;
class StringSet{
public:
//create an empty set
StringSet() = default;
StringSet(int capacity);
//copy a set
StringSet(const StringSet &);
StringSet& operator[](const int);
//Insert a string to the set
bool insert(string);
//Remove a string from the set
bool remove(string);
//Test whether a string is in the set
int find(string) const;
//Get the size of the set
int size() const;
//get string at position i
string get(int i) const;
//Return the set union of the set and another StringSet
StringSet setunion(const StringSet&) const;
//Return the intersection of the set and another StringSet
StringSet intersection(const StringSet&) const;
//Return the set diffference of the set and another StringSet
StringSet difference(const StringSet&) const;
//prevent default copy assignment
StringSet& operator=(const StringSet&) = delete;
int NOT_FOUND = -1;
static constexpr int def_capacity {4};
private:
int arrSize {def_capacity};
int currentSize {0};
unique_ptr<string[]> arr {make_unique<string[]>(def_capacity)};
};
#endif
And this is my implementation of my SetUnion function.
StringSet StringSet::setunion(const StringSet &Array2) const
{
StringSet StringUnion = make_unique<string[]>(arrSize);
if (currentSize > 0)
{
for (auto i=0; i < currentSize; i++)
{
auto s = arr[i];
StringUnion.insert(s);
}
for (auto i=0; i < Array2.currentSize; i++)
{
auto s = Array2[i];
if (StringUnion.find(s) == NOT_FOUND)
{
StringUnion.insert(s);
}
}
}
else
{
auto result = StringSet();
return result; //return empty StringSet}
}
}
Errors:
|error: conversion from 'std::_MakeUniq<std::basic_string<char> []>::__array {aka std::unique_ptr<std::basic_string<char> []>}' to non-scalar type 'StringSet' requested|
error: passing 'const StringSet' as 'this' argument discards qualifiers [-fpermissive]
error: no matching function for call to 'StringSet::find(StringSet&)'
error: no matching function for call to 'StringSet::insert(StringSet&)'
Insert and find work as intended to and I was able to use insert and find functions within my remove function and some others so why can't I use them here?
In your line
StringSet StringUnion = make_unique<string[]>(arrSize);
The RHS uses the c++14 construct that takes an std::size_t, and returns an std::unique_ptr<std::string> internally pointing to an array.
The LHS, however, is a StringSet object.
You did not define a constructor taking such a type, so it's a problem.
Looking at your code, StringSet does have a std::unique_ptr<std::string> member, so you could add a ctor taking such an object, and initializing the member from it. However, it's unclear what would be the benefit of such a ctor, as you already have a ctor
StringSet(int capacity);
which already essentially does the same.
As Leon writes, you should just use this one instead of the line you have
StringSet StringUnion(arrSize);
The errors provides by your compiler seem pretty clear. Let's check them.
conversion from std::make_unique ... to non-scalar type StringSet requested
It's because of the definition of the function std::make_unique, which returns a std::unique_ptr<T>. But you're trying to assign it to a value of type StringSet. There is no constructor or operator for creating a StringSet from a std::unique_ptr, so the compiler complains that he can't do that.
error: no matching function for call to 'StringSet::find(StringSet&)'
Your class StringSet has an operator[] that returns a reference on a StringSet so auto s = Array2[i]; is of type StringSet. But your functions find and insert ask for a std::string. As there is no constructor that can provide implicit conversion from StringSet to std::string, the compiler complains.

invalid use of non-static member function [duplicate]

This question already has answers here:
problem sorting using member function as comparator
(9 answers)
Closed 7 years ago.
I have something like this:
class Bar
{
public:
pair<string,string> one;
std::vector<string> cars;
Bar(string one, string two, string car);
};
class Car
{
public:
string rz;
Bar* owner;
Car(string car, Bar* p);
};
class Foo
{
public:
Foo ( void );
~Foo ( void );
int Count ( const string & one, const string & two) const;
int comparator (const Bar & first, const Bar & second) const;
std::vector<Bar> bars;
};
int Foo::comparator(const Bar & first, const Bar & second) const{
return first.name < second.name;
}
int Foo::Count ( const string & one, const string & two ) const{
int result=0;
Bar mybar = Bar( one, two, "" );
std::vector<Bar>::iterator ToFind = lower_bound(bars.begin(), bars.end(), mybar, comparator);
if (ToFind != bars.end() && ToFind->one == mybar.one ){
result = ...
}
return result;
}
The method Foo::Count should use std::lower_bound() to find element in vector<Bar> according to pair of two strings.
Now the part which doesn't work. To lower_bound() I'm providing method comparator(). I thought it was okay, but g++ says:
c.cpp: In member function ‘int Foo::Count(const string&, const string&) const’:
c.cpp:42:94: error: invalid use of non-static member function
std::vector<Bar>::iterator ToFind = lower_bound(bars.begin(), bars.end(), mybar, comparator);
And the method Count() must stay const...
I'm quite new to C++ because I'm forced to learn it.
Any ideas?
The simplest fix is to make the comparator function be static:
static int comparator (const Bar & first, const Bar & second);
^^^^^^
When invoking it in Count, its name will be Foo::comparator.
The way you have it now, it does not make sense to be a non-static member function because it does not use any member variables of Foo.
Another option is to make it a non-member function, especially if it makes sense that this comparator might be used by other code besides just Foo.
You must make Foo::comparator static or wrap it in a std::mem_fun class object. This is because lower_bounds() expects the comparer to be a class of object that has a call operator, like a function pointer or a functor object. Also, if you are using C++11 or later, you can also do as dwcanillas suggests and use a lambda function. C++11 also has std::bind too.
Examples:
// Binding:
std::lower_bounds(first, last, value, std::bind(&Foo::comparitor, this, _1, _2));
// Lambda:
std::lower_bounds(first, last, value, [](const Bar & first, const Bar & second) { return ...; });
You shall pass a this pointer to tell the function which object to work on because it relies on that as opposed to a static member function.

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.

How to define operator overloading for STL(C++).

I have a question related to operator overloading, and it is easy to define a class as well as its operator overloading function as the following codes illustrate:
typedef std::vector<std::vector<int> > ARRAY;
class ABC
{
public:
ABC():a(0)
{
};
int a;
ABC& operator = (int value)
{
a = value;
return *this;
}
ABC(int value)
{
a = value;
}
};
void obtain_priority_array(const std::vector<double> &weighting, const ABC &priority_array=NULL)
{
}
int main()
{
vector<double> weighting;
weighting.push_back(0.8);
weighting.push_back(0.9);
weighting.push_back(0.6);
weighting.push_back(0.3);
weighting.push_back(0.5);
ABC test;
obtain_priority_array(weighting, test);
return 0;
}
In the above example, class ABC redefined operator = so that the function void obtain_priority_array(const std::vector<double> &weighting, const ABC &priority_array=NULL) can have a default argument const ABC &priority_array=NULL. My question is if the last parameter in the function comes from STL, for example, const std::vector<int> &priority_array=NULL, how can we redefine operator =. Thanks!
EDIT:
void obtain_priority_array(const std::vector &weighting, const std::vector<int> &sample=NULL) failed!
Your misconceptions start with the proposal to add operator= to allow for a default argument of that type. In your example, it is not operator= being called, but ABC(int).
The reason your code isn't being accepted when using std::vector is that NULL translates to 0 (at least it does almost all of the time you'll see it), and the only constructor of std::vector that can take 0, the one taking a count of how many items, is marked explicit.
To fix the immediate problem, the syntax could be changed to:
const std::vector<int> &priority_array = std::vector<int>(0)
However, this introduces different semantics. By your use of NULL, it looks like you were expecting it to represent no vector. This version will provide an empty vector for use if none is given. It will not be no vector at all. If you want that distinction, you should use boost's optional library, or a simple pointer, as references are not the right tool.
References cannot be NULL, your problem has nothing to do with operator overloading. If you want to be able to handle NULL as the default value, switch the parameter type from reference to pointer.
void obtain_priority_array( const std::vector<double>& weighting,
const ABC *priority_array = NULL)
{
if( priority_array == NULL ) {
// blah
} else {
// more blah
}
}
Another option is to use something like Boost.Optional to represent the optional parameter.
typedef boost::optional<ABC> maybe_ABC;
void obtain_priority_array( const std::vector<double>& weighting,
const maybe_ABC& priority_array = maybe_ABC() )
{
if( !priority_array ) {
// blah
} else {
// more blah
}
}
When you use = to create a reference, you're not calling operator= at all. You're initializing the reference.
Instead of using NULL you can create a static instance of the class to represent a null value.
static const ABC ABC_NULL;
void obtain_priority_array(const std::vector<double> &weighting, const ABC &priority_array=ABC_NULL)
{
if (&priority_array == &ABC_NULL) // the default was used
Of course it would be easier to just use a pointer instead of a reference.

How can I make find() work with a set of structs?

I am using a set to hold structs which contain several strings. I want to be able to use the find() functionality of sets. However, since the set is holding structs, it doesn't work. I want find() to look only at one of the strings in the struct. How can this be done?
Here's the code that I tried to use. It works fine except for the part where find() is used:
#include <iostream>
#include <string>
#include <set>
using namespace std;
struct test
{
string key;
string data;
};
bool operator<(const test & l, const test & r)
{
return l.key < r.key;
}
bool operator==(const test & l, const test & r)
{
return l.key == r.key;
}
set<test> s;
int main()
{
test newmember;
newmember.key = "key";
newmember.data = "data";
s.insert(newmember);
s.find("key");
}
Here are the error messages that I get when I try to compile it:
test.cpp:30:7: error: no matching member function for call to 'find'
s.find("key");
~~^~~~
In file included from test.cpp:3:
In file included from /usr/include/c++/4.2.1/set:65:
/usr/include/c++/4.2.1/bits/stl_set.h:429:7: note: candidate function not viable: no known conversion from 'const char [4]' to 'const key_type' (aka 'const test') for 1st argument
find(const key_type& __x)
^
/usr/include/c++/4.2.1/bits/stl_set.h:433:7: note: candidate function not viable: no known conversion from 'const char [4]' to 'const key_type' (aka 'const test') for 1st argument
find(const key_type& __x) const
^
1 error generated.
I suggest you operator< and operator== to your struct instead of overloading the global operator, I find it much cleaner; example:
struct test
{
string key;
string data;
bool operator<(const test& rhs) const
{
return key < rhs.key;
}
bool operator==(const test& rhs) const
{
return key == rhs.key;
}
};
Now on to your real problem - your are passing a string to the find() function, but it only accepts structs of type test. In order to do so, add a constructor for automatic conversion, so the final struct would look like this:
struct test
{
string key;
string data;
test(const std::string& strKey = "", const std::string& strData = "")
: key(strKey),
data(strData) {}
bool operator<(const test& rhs) const
{
return key < rhs.key;
}
bool operator==(const test& rhs) const
{
return key == rhs.key;
}
};
Then passing a string to find() would automatically call the constructor and create a temporary test struct containing only the relevant key. Note that in this special case, the constructor must not be declared explicit.
To be able to put your structs into set you have to specify operator< for your struct. You can make the operator< return result from comparing corresponding string members.
To be able to use find you can specify operator== for your struct to return true if corresponding string members are equal.
Sample:
// code from your question used here
int main()
{
test newmember;
newmember.key = "key";
newmember.data = "data";
test findMember;
findMember.key = "key";
// as operator== and operator< doesn't care about data field we can left it be
// initialized by default constructor
s.insert(newmember);
s.find(findMember);
}
If you want to call find() with string parameter you can provide an implicit constructor from string for your test struct for example like this:
struct test {
//...
test(const string &in_key) : key(in_key) {}
//...
};
But usage of implicit constructors isn't a good technique, because it can lead to some unpredictable conversions somewhere further in your code.
First and foremost, in order to make std::set::find() work with your struct, you don't need to specify operator==, as explained for std::set:
[...] two objects a and b are considered equivalent if neither compares less than the other: !comp(a, b) && !comp(b, a).
However, your actual problem is, that you can't search for parts of your struct by using find() prior to C++14. Prior to C++14, find() expects an element as argument, whose type matches the type of elements stored in the set. For your case, this means that you have to provide an instance of test. However, as your operator< compares only the key variables, you can use a dummy value for the data variable, for example, as follows:
test newmember;
newmember.key = "key";
newmember.data = "data";
s.insert(newmember);
auto it = s.find(test{ "key", "" }); // "" is the dummy variable.
std::cout << it->key << ", " << it->data << std::endl;
Output:
key, data
Please be aware that your operator< which compares only the key variables has a side effect: Two instances of test can only be stored in the set if their key variables are different, even if their data variables are different. For example, if you append the following code to the code above, then newmember will not be inserted (again):
newmember.data = "otherdata";
s.insert(newmember); // Insertion fails!
for (auto const& t : s) {
std::cout << t.key << ", " << t.data << std::endl;
}
Output:
key, data
Consequently, if you want to store multiple elements with the same key, then you might have to choose a different container.
Anyway, if a set of struct is fine for you and you can make use of C++11, then you can also use a lambda expression
instead of defining operator< for your struct:
auto comp = [](const test& t1, const test& t2) { return t1.key < t2.key; };
std::set<test, decltype(comp)> s(comp);
Code on Ideone
C++14
Since C++14, you can use find() to do a "transparent comparison" as explained on std::set::find().
This means, that you can find an element that compares equivalent to the given argument.
In detail this means, that you have to define operator< and your set as follows:
bool operator<(const test& t, const std::string& str) { return t.key < str; }
bool operator<(const std::string& str, const test& t) { return str < t.key; }
bool operator<(const test& t1, const test& t2) { return t1.key < t2.key; }
std::set<test, std::less<>> s;
Then you can perform your find() operation as you would have expected it to work:
test newmember;
newmember.key = "key";
newmember.data = "data";
s.insert(newmember);
auto it = s.find("key");
std::cout << it->key << ", " << it->data << std::endl;
Output:
key, data
Code on Ideone
Only you have to override operator<() function to enhance find function.
In your case simply replace operator<() function as..
bool operator<(const offer& t1) const {
if(this->key< t1.key)
return true;
else if(this->key == t1.key){
if(this->data < t1.data)
return true;
else
return false;
}
else
return false;
}