Search code examples
c++lvalue

Function parameter not an lvalue?


I'm creating a function to get a const int time in seconds and print real time in hours minutes and seconds.
localtime can do that, returning tm structure from time.h. What I panned to do was this:

bool elapsed(const int timeout) {
    //Cast the integer to time_t
    struct tm * now = localtime( (time_t&) &timeout);

    std::wcout<<L"Time elapsed: "<<now->tm_hour<<':'<<now->tm_min<<':'<<now->tm_sec<<"\r\n";
    return timeout>600;
}

But this throws an error: (time_t&)&timeout - expression must be an lvalue

Besides troubling me with an before a consonant, the error makes no sense to me. I've read that lvalues are things with names. Is this case an exception?


Solution

  • The localtime function requires an argument of type time_t*, a pointer to a time_t object.

    In your current code, you don't have a time_t object, so you can't create a valid pointer to such an object.

    Define a local time_t object, initialized to the (converted) value of timeout, and pass that object's address to localtime:

    bool elapsed(const int timeout) {
        time_t t_timeout = timeout;
        struct tm * now = localtime(&t_timeout);
        // ...
    

    But if possible, it would be cleaner to declare your timeout parameter as a time_t in the first place:

    bool elapsed(time_t timeout) {
        struct tm * now = localtime(&timeout);
        // ...
    

    This all assumes that your timeout value is a sensible value to pass to localtime(). It very likely isn't. A time_t value represents a time, not a duration; on many systems, it's the number of seconds since 1970-01-01 00:00:00 GMT. A timeout value of 3600, for example, would represent 1:00 AM on Jan 1, 1970. localtime as the name implies, treats it as a local time, so depending on your time zone it might be, for example, 4pm on December 31. And that's all assuming the most common interpretation of time_t values; the language standard only guarantees that it's an arithmetic type able to represent times somehow.

    If your goal is to break down your timeout value into hours, minutes, and seconds, just do the arithmetic yourself:

    int seconds = timeout % 60;
    int minutes = timeout / 60; // integer division truncates
    int hours = minutes / 60;
    minutes %= 60;
    

    As for why you got that particular error message: a reference has to refer to an object. When I compile your code with g++ I get:

    c.cpp:6:45: error: invalid cast of an rvalue expression of type
                'const int*' to type 'time_t& {aka long int&}'
    

    An lvalue is, roughly, an expression that refers to an object. Given

    int x = 42;
    

    the expression x is an value, but 42 and x+1 are not.

    The expression timeout is an lvalue, because it's the name of an object. The expression &timeout is not an lvalue. It evaluates to a value (the address of timeout, of type time_t*), but does not refer to an object, because you don't have an an object of type time_t* for it to refer to.

    You can convert (cast) an lvalue to a reference type. Since &timeout is not an lvalue, the cast is invalid.

    But that doesn't really matter, because converting to a reference doesn't make sense in this context. Telling you how to do the cast correctly would not be helpful, since no cast is required or appropriate for what you're trying to accomplish. All you need is a little simple arithmetic -- no pointers, no references, no casts.