Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

(Why) are std::chrono literals lvalues?

Tags:

c++

Basically the example says it all: assigning to a std::chrono literal - does not give an error message. I tried on gcc, clang and vc, (c++17) all these compilers accept it. Is this intended behaviour?

#include <chrono>
using namespace std::chrono_literals;

int main()
{
    10ms  += 1ms;       // would expect error here
    12ms = 10ms;        // and here
    // 3 = 4;           // like here
    return 0;
}
like image 810
DanRechtsaf Avatar asked Aug 28 '20 11:08

DanRechtsaf


2 Answers

are std::chrono literals lvalues?

No. std::chrono_literals are prvalues.

Is this intended behaviour?

It is as the standard has specified.

These values are of class types. And rvalues of class types can be assigned1 as long as the class is assignable, and their assignment operator overload is not lvalue ref qualified. Assignment operators of classes, like all member functions, are not lvalue ref qualified by default, and hardly any standard class has such qualified overload2 - I don't know of any but if there are, std::chrono::duration isn't one of them.

Note that the literal isn't modified. The left hand operand is a temporary object which in this case is discarded.


Another simple example of assigning an rvalue:

std::string{"temporary"} = "this is pointless, but legal";

1 This is actually a useful feature, although there aren't many use cases. Here is a useful example:

std::vector<bool> vec{true, false, true};
vec[1] = true; // vec[1] is an rvalue

2 Prior to C++11, there were no ref qualifiers, and thus all rvalues of assignable class types could be assigned. To maintain backward compatibility, the default cannot be changed, nor can pre-existing classes be changed (at least not without a deprecation period). std::chrono::duration was added in C++11, so it could have had qualified assignment operator, but standard authors appear to prefer not qualifying their assignment operators.

like image 85
eerorika Avatar answered Nov 20 '22 19:11

eerorika


The std::chrono literals are not lvalues. They may appear as such, but what you are seeing is actually invocation of assignment operators by r-values. Why is this allowed? Specifically, because e.g. the member assignment operator

constexpr duration& operator+=(const duration& d)

as specified by [time.duration.arithmetic]/9, does not use any ref-qualifiers, meaning it does not reject being invoked by r-values.

Compare with the following example (implemented as duration above)

struct Foo {
    unsigned long long i;
    constexpr Foo& operator+=(const Foo& d) { return *this; }
};

constexpr Foo operator"" _Foo (unsigned long long i) {
    return Foo{i};
}

int main() {
    1_Foo += 2_Foo;  // OK!
}

as compared to the following example, which explicitly makes use of a & ref-qualifier to implicitly ban invocation on r-values:

struct Foo {
    unsigned long long i;
    constexpr Foo& operator+=(const Foo& d) & { return *this; }
};

constexpr Foo operator"" _Foo (unsigned long long i) {
    return Foo{i};
}

int main() {
    1_Foo += 2_Foo;  // error: no viable overloaded '+='
}

or, the following example, which explicitly deletes the r-value overload:

struct Foo {
    unsigned long long i;
    constexpr Foo& operator+=(const Foo& d) & { return *this; }
    constexpr Foo& operator+=(const Foo& d) && = delete;
};

constexpr Foo operator"" _Foo (unsigned long long i) {
    return Foo{i};
}

int main() {
    1_Foo += 2_Foo;  // error: overload resolution selected deleted operator '+='
}

For details regarding r-value qualifiers and how they could make sense to apply in order to ban to-rvalue-assignment, see e.g. the following Q&A:

  • What does the & (ampersand) at the end of member function signature mean?
like image 28
dfrib Avatar answered Nov 20 '22 20:11

dfrib