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;
}
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.
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:
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With