Почему действует std :: chrono :: duration :: operator * = не как встроенный * =? - PullRequest
0 голосов
/ 22 февраля 2019

Как описано в std :: chrono :: duration :: operator + = подпись

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

Меня это удивляет.Я бы предположил, что литерал длительности можно использовать как любой другой встроенный, но это не так.

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

Вывод

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

Почему интерфейс был выбран таким образом?На мой взгляд, интерфейс типа

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

даст более интуитивно понятные результаты.

РЕДАКТИРОВАТЬ:
Спасибо за ваши ответы, я знаю, что реализация ведет себятаким образом, и как я мог справиться с этим.У меня вопрос, , почему так устроен.

Я ожидаю, что преобразование в int произойдет в конце операции.В следующем примере оба операнда удваиваются до того, как произойдет умножение.Промежуточный результат 4.5 впоследствии конвертируется в int, так что результат равен 4.

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

Я ожидаю, что std::duration будет вести себя так же.

Ответы [ 3 ]

0 голосов
/ 22 февраля 2019

Глядя на реализацию operator*=:

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

, оператор получает const _Rep&.Это происходит от std::duration, который выглядит следующим образом:

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

Так что теперь, если мы посмотрим на определение std::chrono::minutes:

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

Ясно, что _Rep является int.


Поэтому, когда вы звоните operator*=(const _Rep& _Right) 1.5f, вы получаете int, что равняется 1 и, следовательно, не влияет на любые умножения с самим собой.

Так что вы можете сделать?

вы можете разделить его на m = m * 1.5f и использовать std::chrono::duration_cast для приведения из std::chrono::duration<float, std::ratio> в std::chrono::duration<int, std::ratio>

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

150% от 10 минут: 15 минут


, если вам не нравится всегда приводить его, используйте float в качестве первого аргумента шаблона:

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

или даже быстрее - auto m = 10.0min; m *= 1.5f;, как @NathanOliver ответил: -)

0 голосов
/ 22 февраля 2019

У меня вопрос, почему он спроектирован таким образом.

Он был спроектирован таким образом (по иронии судьбы), потому что вычисления на основе интегралов предназначены для получения точных результатов или не компилируются,Однако в этом случае библиотека <chrono> не контролирует, какие преобразования применяются к аргументам до для привязки к аргументам.

В качестве конкретного примера рассмотрим случай, когда mинициализируется 11min и предполагает, что у нас был шаблон operator*=, как вы предлагаете. точный ответ теперь 16.5min, но целочисленный тип chrono::minutes не способен представлять это значение.

В превосходном варианте будет иметь эту строку:

m *= 1.5f;  // compile-time error

не компилируется.Это сделало бы библиотеку более самосогласованной: арифметика на основе интегралов либо точна (или требует duration_cast), либо не компилируется.Это можно было бы реализовать, и ответ на вопрос, почему это не было сделано, заключается в том, что я просто не думал об этом.


Если вы (или кто-либо другой) достаточно сильно чувствуете этопопытаться стандартизировать ошибку времени компиляции для вышеприведенного утверждения, я был бы готов выступить в поддержку такого предложения в комитете.

Эти усилия будут включать:

  • Реализацияс модульными тестами.
  • Создание его, чтобы получить представление о том, сколько кода он сломает, и обеспечение того, чтобы он не нарушал код, не предназначенный.
  • Напишите статью и отправьте ее на C ++комитет, нацеленный на C ++ 23 (слишком поздно нацеливаться на C ++ 20).

Самый простой способ сделать это - начать с реализации с открытым исходным кодом, такой как gcc libstdc ++ илиlibc ++ от llvm.

0 голосов
/ 22 февраля 2019

Проблема здесь

auto m = 10min;

дает вам std::chrono::duration, где rep - целочисленный тип со знаком.Когда вы делаете

m *= 1.5f;

, 1.5f преобразуется в тип rep, и это означает, что оно усекается до 1, что дает вам то же значение после умножения.

Чтобы исправить это, вам нужно использовать

auto m = 10.0min;

, чтобы получить std::chrono::duration, который использует тип с плавающей запятой для rep и не будет усекать 1.5f при выполнении m *= 1.5f;.

...