Why doesn't std::tie work with const class methods? - c++

#include <string>
#include <tuple>
#include <stdexcept>
using namespace std;
class Date
{
public:
Date() {};
Date(int new_year, int new_month, int new_day)
{
year = new_year;
if ((new_month < 1) || (new_month > 12))
{
throw runtime_error("Month value is invalid: " + to_string(new_month));
}
month = new_month;
if ((new_day < 1) || (new_day > 31))
{
throw runtime_error("Day value is invalid: " + to_string(new_day));
}
day = new_day;
}
int GetYear() const
{
return year;
}
int GetMonth() const
{
return month;
}
int GetDay() const
{
return day;
}
private:
int year;
int month;
int day;
};
bool operator < (const Date &lhs, const Date &rhs)
{
auto lhs = tie(lhs.GetYear(), lhs.GetMonth(), lhs.GetDay());
auto rhs = tie(rhs.GetYear(), rhs.GetMonth(), rhs.GetDay());
return lhs < rhs;
}
I'm trying to create a class for storing the date (year, month, day). In order to use this class in a map, I want to overload the comparison operator. Unfortunately, the code above does not work. The compiler gives me an error
error: cannot bind non-const lvalue reference of type 'int&' to an rvalue of type 'int'|
The error apparently occurs in tie function. Once I change it to make_tuple everything compiles and works. I also checked that if I declared variables year, month and day as public I could write something like
auto lhs = tie(lhs.year, lhs.month, lhs.day);
and this would also work.
I don't understand why. Does anyone have ideas?

Your member functions return copy of the members(aka temprory rvalue), and std::tie has argument list, which try to bind by non-cost lvalue ref.
template< class... Types >
constexpr std::tuple<Types&...> tie( Types&... args ) noexcept;
// ^^^^^^^^^^^^^^^^
This is simply not possible as per standard, hence the error!
The std::make_tuple on the other hand has forwarding reference. Therefore, no issues.
template< class... Types >
std::tuple<VTypes...> make_tuple( Types&&... args );
// ^^^^^^^^^^^^^^^^
When the members are public, they became lvalues.
I would suggest, make the operator< friend of the class instead.
class Date
{
public:
// .... other code
friend bool operator<(const Date& lhs, const Date& rhs) noexcept
{
return std::tie(lhs.year, lhs.month, lhs.day) < std::tie(rhs.year, rhs.month, rhs.day);
}
private:
int year;
int month;
int day;
};

The entire purpose of std::tie is to create a tuple of lvalue references. Your member functions return int which aren't lvalues, and so you get an error.
Making the member variables public and using them directly as arguments to tie works because variables are lvalues.

Related

STABLE_PARTITION problems: no matching function to call to "swap"

somehow I can't use stable_partition algorithm on
vector<pair<Class, string>>.
I can re-organize the code to get what I want, but for me (as I am new to C++) it's more "WHY" and not "HOW" question. will be glad if you clarify this behavior:
First, class Date (you can omit it and come look at it later):
#include <iostream>
#include <string>
#include <vector>
#include <algorithm>
#include <set>
#include <vector>
using namespace std;
class Date {
public:
Date(int new_year, int new_month, int new_day) {
year = new_year; month = new_month; day = new_day;
}
int GetYear() const {return year;}
int GetMonth() const {return month;}
int GetDay() const {return day;}
private:
int year, month, day;
};
bool operator<(const Date& lhs, const Date& rhs) {
return vector<int>{lhs.GetYear(), lhs.GetMonth(), lhs.GetDay()} <
vector<int>{rhs.GetYear(), rhs.GetMonth(), rhs.GetDay()};
}
bool operator==(const Date& lhs, const Date& rhs) {
return vector<int>{lhs.GetYear(), lhs.GetMonth(), lhs.GetDay()} ==
vector<int>{rhs.GetYear(), rhs.GetMonth(), rhs.GetDay()};
}
SO THIS IS THE CLASS WITH THE TROUBLE:
class Database {
public:
void Add(const Date& date, const string event){
storage.push_back(make_pair(date, event));
set_dates.insert(date);
}
void Print(ostream& s) const{
for(const auto& date : set_dates) {
// TROUBLE IS HERE:
auto it = stable_partition(begin(storage), end(storage),
[date](const pair<Date, string> p){
return p.first == date;
});
};
}
private:
vector<pair<Date, string>> storage;
set<Date> set_dates;
};
When compiled, it returns a lot of problems of same kind:
I've tried the same code on vector<pair<int, string>> (used stable_partition with lambda {return p.first == _int; } and it worked.
Would appreciate your help
The std::stable_partition function is supposed to modify the vector. However, you are calling it in a const member function, so storage is const there. This can't work.
Solution: Don't make Print const, or use std::stable_partition on a copy of storage. Neither is a great solution, so you should probably rethink your design.
You need to define overloading operator= for Date class as well. It will work if you do that stuff.
class Date {
public:
Date(int new_year, int new_month, int new_day) {
year = new_year; month = new_month; day = new_day;
}
// Need to define overloading operator=
Date& operator=(const Date& rhs)
{
}
int GetYear() const {return year;}
int GetMonth() const {return month;}
int GetDay() const {return day;}
private:
int year, month, day;
};

Declaring priority_queue for user-defined class

I have a class named Customer which is overloading the < operator:
bool Customer::operator<(const Customer &other) {
return this->price < other.price;
}
but when I try to initialize the priority queue I get pages of errors.
Customer c1(10,5,12,30);// last parameter is price
Customer c2(10,5,12,2);
priority_queue<Customer , vector<Customer> , less<Customer> > barQ;
barQ.push(c2);
barQ.push(c1);
cout<<barQ.top().price;
Then, i accidentally found out that when i initialize it in following way:
Customer c1(10,5,12,30);
Customer c2(10,5,12,2);
priority_queue<Customer* , vector<Customer*> , less<Customer*> > barQ;
barQ.push(&c2);
barQ.push(&c1);
cout<<barQ.top()->price;
I got no errors and it works well.
So my question is that what's the difference between Customer & Customer*?
I thought it should work when I declare it with Customer, NOT Customer* , why it works otherwise?
The signature of std::less::operator() is (taken from cppreference):
bool operator()( const T& lhs, const T& rhs ) const; // (until C++14)
constexpr bool operator()( const T& lhs, const T& rhs ) const; //(since C++14)
Note the it takes both parameters as const, thus it can only call a const operator<:
bool Customer::operator<(const Customer &other) const { // <--- add const here
return this->price < other.price;
}
Your second code is not really doing what you want it to do, because it uses the built-in operator< for pointers.
you are missing const in operator overload
bool operator<(const Customer &other) const {
return this->price < other.price;
}
you can also use your own comparator instead of std::less. Here is how we can write it.
template<typename type>
struct mycomp {
bool operator()(const type & first, const type & second) const {
return first.price < second.price;
}
};
struct Customer {
int price;
};
int main(){
Customer c1{3};// last parameter is price
Customer c2{2};
priority_queue<Customer , vector<Customer> , mycomp<Customer> > barQ;
barQ.push(c2);
barQ.push(c1);
cout<<barQ.top().price;
return 0;
}
set::less<> doesn't gives an error with Customer* pointer because pointers comparisons can happen as int and it doesn't look for the custom implementation. which is not the case with Customer.

Overloading the += operator

I have the following code to overload the + and += operators respectively for the class Date. The operator + was successfully overloaded, and it will take an integer n and increase Date objects by n days. This is done by applying the next_day function n times.
inline Date operator+(Date d, int n)
{
for(char j=1; j<=n; j++){
d=d.next_day(d);
}
return d;
}
inline Date operator+=(Date d, int n)
{
Date p=d+n;
return p;
}
Having overloaded the + operator, I'm using it to define the overloading of += as well. But although no errors occurred in compilation, when I use the overloaded += it doesn't seem to have any effect.
Here's my main.cpp:
#include <iostream>
#include "Date.h"
using namespace std;
int main() {
Date Initialday = Date (12,1,2012);
Initialday+=1;
cout <<"Next day = "<< Initialday <<endl;
return 0;
}
Running the main function still gives me 12/1/2012 instead of 12/2/2012. What am I doing wrong? Note: I've already overloaded << to output Date objects in a readable format, so I don't think that's the issue.
The simple fix is to take your Date object in by reference, modify it, and return it by reference. That's the expected behavior of operator+=.
inline Date& operator+=(Date &d, int n)
{
d = d + n;
return d;
}
However, to implement operator+= in terms of operator+ is backwards. It should be the other way around. operator+= should act on the members of the object, changing them directly. Then operator+ should be implemented in terms of that:
inline Date& operator+=(Date& lhs, int rhs)
{
... // code here to modify lhs directly
return lhs;
}
inline Date operator+(Date lhs, int rhs)
{
return lhs += rhs;
}
The main problem is that your += is creating a new Date object and returning it. That is the wrong semantics, plus you do not assign that return value to anything. A += operator should act on the instance it is applies to and return it by reference:
inline Date& operator+=(Date& d, int n) {
return d = d + n;
}
Usually, this would be implemented as a member function, with + implemented in terms of +=:
class Date
{
public:
Date& operator+=(int n) {
// perform whatever operation is required using
// the state of the instance.
return *this;
}
};
inline Date operator+(Date lhs, int rhs) {
return lhs += rhs; // calls member +=
}
The cleanest way would be to provide a time duration class, and implement all the operators in terms of a Date and a TimeDuration:
struct TimeDuration { .... };
class Date
{
public:
Date& operator+= (const TimeDuration& rhs) { .... }
};
inline Date operator+(Date lhs, const TimeDuration& rhs) { return lhs += rhs; }

Non-static function as parameter to another function in C++

I've made a class student with subclass comparator. Constructor of comparator takes one argument called cmp_mode that specifies how we should compare students.
class student
{
public:
std::string name;
int course, group;
student(std::string name,
int course,
int group): name(name)
{
this->course = course;
this->group = group;
}
enum cmp_mode
{
NAME,
COURSE,
GROUP
};
class comparator
{
cmp_mode mode;
public:
comparator(cmp_mode mode)
{
this->mode = mode;
}
bool compare(student s1, student s2)
{
if (mode == NAME)
return s1.name < s2.name;
else if (mode == COURSE)
return s1.course < s2.course;
else if (mode == GROUP)
return s1.group < s2.group;
else
throw "Oh god! We can't compare variables using these keys!";
}
};
};
Also, I've created a list of students and now I want to sort this list using comparator subclass.
std::list<student> l;
student st1("Anya", 2, 5);
student st2("Misha", 4, 2);
student st3("Yasha", 1, 3);
l.push_back(st1);
l.push_back(st2);
l.push_back(st3);
student::comparator by_group(student::GROUP);
l.sort(by_group.compare);
But I'm getting the following error.
ERROR: Reference to non-static member function must be called.
So what should I do? How can I adjust sorting in the better way?
I'm starting from your comment:
I thought that I can write compare function for each case, but it seems even worse.
Why worse? Personally I think it is faster to run (at least faster to compile) and easier to maintain (shorter functions).
Isn't this much simpler to write:
l.sort(student::compare_by_group);
And this implementation easier to maintain:
class student
{
...
static bool compare_by_name(const student& s1, const student& s2)
{
return s1.name < s2.name;
}
static bool compare_by_course(const student& s1, const student& s2)
{
return s1.course < s2.course;
}
static bool compare_by_group(const student& s1, const student& s2)
{
return s1.group < s2.group;
}
// Add the followings only if you really need this stuff
// e.g. to get comparator type from elsewhere
enum cmp_mode
{
NAME,
COURSE,
GROUP
};
static bool compare(cmp_mode, const student& s1, const student& s2)
{
switch(cmp_mode) {
case NAME: return compare_by_name(s1, s2);
...
}
}
};
Like Piotr, I'd recommend to write comparators for each property, which is faster and less error-prone. However, I'd recommend to use functor objects instead of the static functions:
struct compare_by_name {
bool operator()(const student& a, const student& b) const {
return a.name < b.name;
}
};
If you need the comparators only once or maybe twice and you're using C++11, prefer an inline lambda:
l.sort([](const student& a, const student& b){ return a.name < b.name; });
If you absolutely need a dynamic comparator, write it as normal functor object, i.e., define an operator():
bool operator()(const student& a, const student& b) const {
switch (mode) {
case NAME:
return a.name < b.name;
// No default case, the constructor (if at all) should check whether the mode
// is valid.
}
}
Non-static member functions have an implicit parameter of the type of which the function is a member. They need an instance of this type in order to work. You can use std::bind (or boost.bind if you don't have C++11 support) to "bind" a student::comparator object to the first parameter of the comparison function:
student::comparator by_group(student::GROUP);
using namespace std::placeholders;
auto comp = std::bind(student::comparator::compare, &by_group, _1, _2);
l.sort(comp);
In the code above, the by_group object is bound as first argument to the student::comparator::compare function, and the resulting function object takes two student objects and returns a bool:
std::cout << std::boolalpha;
const student& s1 = l[0];
const student& s2 = l[1];
std::cout << comp(s1, s2) << "\n"; // prints "true" or "false".
I would also advise you to change the signature of the comparison member function to
bool compare(const student& s1, const student& s2) const;
There is no reason to pass by value, and there is no reason for the member function not to be const.

Why i need to use the const function in the Less Traits

why i need to use the const function in the less traits?
for example, why i must use const in Age or ID member function.
#include <iostream>
#include <iterator>
#include <algorithm>
#include <vector>
#include <string>
#include <set>
using namespace std;
class Person
{
public:
Person(int id, string name, int age):m_iID(id), m_strName(name), m_iAge(age){};
int Age() const {return m_iAge;}
int ID() const {return m_iID;}
void Display();
friend ostream& operator<<(ostream& out, Person& person);
private:
int m_iID;
string m_strName;
int m_iAge;
};
void Person::Display()
{
cout<<m_iID<<" "<<m_strName<<" "<<m_iAge<<endl;
}
ostream& operator<< (ostream& out, Person& person)
{
out<<person.m_iID<<" "<<person.m_strName<<" "<<person.m_iAge<<endl;
return out;
}
int SumPersonAge(int iSumAge, Person& person)
{
return iSumAge + person.Age();
}
template <typename Type>
void Display(Type t1)
{
cout<<t1<<endl;
}
class LessPerson
{
public:
template <typename Type>
bool operator()(Type& t1, Type& t2)
{
return t1.ID() < t2.ID();
}
};
int main()
{
set<Person, LessPerson> s1;
Person p1(1234, "Roger", 23);
Person p2(1235, "Razor", 24);
s1.insert(p1);
s1.insert(p2);
for_each(s1.begin(), s1.end(), Display<Person>);
}
if i remove the const the keyword in Age or ID function, the compiler will report me Error cannot convert 'this' pointer from 'const Person' to 'Person &'.
The answer is that the set will pass two const reference to Person objects to your comparator, and you cannot call a non-const function on a const variable.
You might be surprised as there seems not to be any const in the declaration of the functor:
struct LessPerson
{
template <typename Type>
bool operator()(Type& t1, Type& t2)
{
return t1.ID() < t2.ID();
}
};
But there is, it is just not explicit in the code. Inside the set implementation there are two const Person& references (call them r1, r2) and a LessPerson comparator (call it compare) and the code does something in the lines of if ( comparator(r1,r2) ). The compiler finds the templated operator() and tries to infer the types ending up with the type substitution: Type == const Person.
Why does the set use const references rather than plain modifiable references? Well, that is a different issue. The set is implemented as a sorted balanced tree, with each node containing the key. Now because a change in the key would break the order invariant, the keys are themselves const objects, ensuring that you cannot break the invariants.
That const keyword means that the function does not modify its object, this. Only const functions may be called from const objects. So, the compiler is telling you that you are trying to call a non-const member function from a const object.
You appear to be avoiding the const keyword, but it creeps in when the library calls your template function:
template <typename Type>
bool operator()(Type& t1, Type& t2)
{
return t1.ID() < t2.ID();
}
Type is passed as const Person.
const is not easy to get rid of without cheating, and it will always creep in. It's better to go with the flow and add const everywhere you take a reference that doesn't change an object (which is most of them).
Because the type passed to LessPerson is const Person.
So basically, LessPerson is roughly
class LessPerson   
{
public:
bool operator()(const Person& t1, const Person& t2)
{
return t1.ID() < t2.ID();
}
};
You can't call non-const methods on const objects. It's a rule of C++.