Home > OS >  Why doesn't std::tie work with const class methods?
Why doesn't std::tie work with const class methods?

Time:11-13

#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?

CodePudding user response:

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

CodePudding user response:

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