Why acts std::chrono::duration::operator*= not like built-in *=?

std chrono convert duration to milliseconds
std::chrono::steady_clock duration
std::chrono::time_point
std::chrono::system_clock::duration
std::chrono has not been declared
std::chrono::days
std chrono time_duration
c++ std chrono time point

As described in std::chrono::duration::operator+= the signature is

duration& operator*=(const rep& rhs);

This makes me wonder. I would assume that a duration literal can be used like any other built-in, but it doesn't.

#include <chrono>
#include <iostream>

int main()
{
    using namespace std::chrono_literals;
    auto m = 10min;
    m *= 1.5f;
    std::cout << " 150% of 10min: " << m.count() << "min" << std::endl;

    int i = 10;
    i *= 1.5f;
    std::cout << " 150% of 10: " << i << std::endl;
}

Output is

150% of 10min: 10min
150% of 10: 15

Why was the interface choosen that way? To my mind, an interface like

template<typename T> 
duration& operator*=(const T& rhs);

would yield more intuitive results.

EDIT: Thanks for your responses, I know that the implementation behaves that way and how I could handle it. My question is, why is it designed that way.

I would expect the conversion to int take place at the end of the operation. In the following example both operands get promoted to double before the multiplications happens. The intermediate result of 4.5 is converted to int afterwards, so that the result is 4.

int i = 3;
i *= 1.5;
assert(i == 4);

My expectation for std::duration would be that it behaves the same way.

Why acts std::chrono::duration::operator*= not like built-in *=?, would not be able to guarantee that only one of expr2 and expr3 was executed. It is important to consider std::common_type when discussing changes to the The built-in conditional operator only evaluates the expression chosen by the Mention chrono::duration and other numeric types as motivation. Why was the interface choosen that way? To my mind, an interface like. template<typename T> duration& operator*=(const T& rhs); would yield more intuitive results. EDIT: Thanks for your responses, I know that the implementation behaves that way and how I could handle it. My question is, why is it designed that way.

The issue here is

auto m = 10min;

gives you a std::chrono::duration where rep is a signed integer type. When you do

m *= 1.5f;

the 1.5f is converted to the type rep and that means it is truncated to 1, which gives you the same value after multiplication.

To fix this you need to use

auto m = 10.0min;

to get a std::chrono::duration that uses a floating point type for rep and wont truncate 1.5f when you do m *= 1.5f;.

operator+,-,*,/,%(std::chrono::duration , It is important to consider std::common_type when discussing changes to the con - ditional operator. All operators act using built-in syntax, the conditional operator is the only candidate. The motivation is not as strong as in the above case, since in most std::chrono::time_point<Clock, Duration>. Why acts std::chrono::duration::operator*= not like built-in *=? As described in std::chrono::duration::operator+= the signature isThis makes me wonder. I would assume that a duration literal can be used like any other built-in, but it doesn't.

Looking at the implementation of operator*=:

_CONSTEXPR17 duration& operator*=(const _Rep& _Right)
    {   // multiply rep by _Right
    _MyRep *= _Right;
    return (*this);
    }

the operator takes a const _Rep&. It comes from std::duration which looks like:

template<class _Rep, //<-
    class _Period>
    class duration
    {   // represents a time Duration
    //...

So now if we look at the definition of std::chrono::minutes:

using minutes = duration<int, ratio<60>>;

It is clear that _Rep is an int.


So when you call operator*=(const _Rep& _Right) 1.5f is beeing cast to an int - which equals 1 and therefore won't affect any mulitiplications with itself.

So what can you do?

you can split it up into m = m * 1.5f and use std::chrono::duration_cast to cast from std::chrono::duration<float, std::ratio> to std::chrono::duration<int, std::ratio>

m = std::chrono::duration_cast<std::chrono::minutes>(m * 1.5f);

150% of 10min: 15min


if you don't like always casting it, use a float for it as the first template argument:

std::chrono::duration<float, std::ratio<60>> m = 10min;
m *= 1.5f; //> 15min

or even quicker - auto m = 10.0min; m *= 1.5f; as @NathanOliver answered :-)

operator==,!=,<,<=,>,>=,<=>(std::chrono::duration , inline; int& Array::operator[] (unsigned i) // Some people don't like this syntax; {; // . sizeof cannot be overloaded because built-in operations, such as incrementing N and m are names known to the compiler and :: performs a (compile time) scope it means the two objects should ideally act like they have the same state. Why acts std::chrono::duration::operator*= not like built-in *=? Convert all element in the nested list to integers; What is the correct way to temporarily cast void* for arithmetic? Getting first n unique elements from Python list; Calling vs invoking a function; Why does std::hex lead to memory corruption vector.size()?

[PDF] Making operator?: overloadable [P0917R3], std::chrono::duration operators duration& operator+= (const duration& rhs); duration& operator-= (const duration& rhs); duration& operator*= (const rep& r);� Please not that there is a difference in how C and C++ deal with incompatible struct defintions. The differing definitions of struct foo in your posted code, is ok in a C program as long as you don't mix their usage. However, it is not legal in C++. In C++, they have external linkage and must be defined identically.

[PDF] Making operator?: overloadable [P0917R2], The latest version of the RB 211 is not returning the fuel performance that Rolls that the standard Pratt & Whitney engine was giving operators a lot of trouble, with General Electric saw the gap at about the same time as Rolls, and has since Rolls-Royce is likely to be left with only the TriStar, which is being built at less� D/ (14362): HostConnection::get() New Host Connection established 0x9ffa3dc0, tid 14398 D/EGL_emulation(14362): eglMakeCurrent: 0x9fd86c00: ver 3 0 (tinfo 0x9fff1110) W/zygote (14362): Unsupported class loader W/zygote (14362): Skipping duplicate class check due to unsupported classloader I/DynamiteModule(14362): Considering local module com

Operator Overloading, C++ FAQ, Now you can own a famous 17-jewel ARISTO CHRONOGRAPH-the most is a precision built timepiece oi' deEendable accuracy, with enuine pigskin strap Two siiiall it for 10 days on our MoneyBack Guarantee If not delighted with your ARISTO. The other service provides a representative who will act as advisor to the� In my testing, it seems the explicit bool operator does the trick. But I'm not sure. It looks like the desired overloads are being called. But I have a built-in aversion to casting operators burned into me in dark old days of the 1990s. So my question: Is this really is as safe as it seems?

Comments
  • rep is an arithmetic type representing the number of ticks, and is /*signed integer type of at least 29 bits*/ for minutes, not float.
  • POD is not the right term here. You probably meant "built in".
  • Besides the terminology I 100% agree that a template <class T> duration& operator*=(const T& rhs); version would be more intuitive. As e.g. int i = 10; i *= 1.5; also results in i == 15, I'm wondering why the C++ dogma of "custom types should be usable like built in ones" is not followed here.
  • How is "Integral-based arithmetic is either exact (or requires duration_cast) or does not compile." (more) consistent with the built-in example I provided above, which complies and which isn't exact? Wouldn't a compile error just provide another kind of inconsistency? (I will take it into consideration to write a proposal. I'm new to the process, so it may take some time to get familiar with the process. :-) )
  • I changed "consistent" to "self-consistent" in my answer. I.e. this change would make the library more consistent with itself. In other arithmetic expressions, the result of integral and floating-point mixed expressions result in a floating-point based result. And in the chrono library, floating point based types won't convert to integral based types without an explicit named conversion (e.g. duration_cast or time_point_cast, and in C++17, floor, ceil and round).
  • @MichaelReinhardt 10min * 1 is exactly 10min. The loss of precision happens before the call to the operator so as to satisfy the signature. This is, for historical reasons / inherited from C), and outside of chrono's control. Yes, as H.H. stated, it could be fixed (or worked around), e.g. by adding overloads that are = deleted, but IMO C and C++ should just finally deprecate potentially lossy implicit conversions with the intent to remove them. Yes, there would be another epic rant by Linus Torvalds on how much language committees suck, but in the end the community should be better off.