chrono 解答ページ | Programming Place Plus 新C++編

トップページ新C++編chrono

このページの概要 🔗

このページは、練習問題の解答例や解説のページです。



解答・解説 🔗

問題1 (確認★) 🔗

次のプログラムが出力する2つの値は true、false のどちらになりますか?

#include <chrono>
#include <iostream>

int main()
{
    std::chrono::duration<int, std::milli> d1 {1000};
    std::chrono::duration<int, std::micro> d2 {1000};

    std::cout << std::boolalpha << (d1 == d2) << "\n";
    std::cout << std::boolalpha << (d1.count() == d2.count()) << "\n";
}


std::chrono::duration は2つの時間の間隔を表現する型です(本編解説)。1つ目のテンプレート仮引数は内部の値の表現に使う型、2つ目のテンプレート仮引数は分解能を表しています。したがって、d1 は内部的には int型で、ミリ秒単位の分解能、d2 のほうは内部的には int型で、マイクロ秒の分解能によって時間の間隔が表現できることになります。

d1d2 を定義するときいずれも 1000 を与えています。この値は、この duration の分解能で時間を刻んだ回数を意味します。d1 の分解能はミリ秒なので 1000回刻めば 1000ミリ秒、d2 の分解能はマイクロ秒なので 1000マイクロ秒を意味しています。このように d1d2 が表している時間は異なっていますから、d1 == d2 は false になります。

countメンバ関数が返す値は、時間をどれだけの回数刻んできたかを表現したものです。この考え方では、d1d2 も 1000回ずつの時間を刻んでいるので、いずれも 1000 を返します。したがって、d1.count() == d2.count() は true になります。

実行結果:

false
true

問題2 (基本★) 🔗

標準出力から指定された秒数だけ待ったあと、“Hello” と出力するプログラムを作成してください。


たとえば次のように作成できます。

#include <chrono>
#include <iostream>

int main()
{
    int wait_seconds {};
    std::cin >> wait_seconds;

    auto print_timing = std::chrono::steady_clock::now() + std::chrono::seconds{wait_seconds};

    while (true) {
        auto now = std::chrono::steady_clock::now();
        if (now >= print_timing) {
            std::cout << "Hello.\n";
            break;
        }
    }
}

実行結果:

5  <-- 入力した値
Hello.  <-- 入力してから5秒後に出力

標準出力から秒数を受け取ったら、「現在の時間+受け取った時間」によって、出力を行うべきタイミングを計算できます。

現在の時間は、std::chrono::steady_clock::now() を呼ぶことで取得できます(本編解説)。steady_clock 以外のクロックを使っても構いませんが、時間が逆行しない保証があるクロックを選ぶと確実ではあります(本編解説)。nowメンバ関数は time_point で結果を返します。time_point は、時間軸上のある1点を表現する型です(本編解説)。

標準出力から受け取った秒数は、そのままでは int型の整数に過ぎません。これを「秒」として扱うために、std::chrono::seconds に渡しています(本編解説)。「秒」を表す型になったことで、time_point に加算することができます。加算結果もまた time_point です。

あとは、現在の時間を繰り返し調べながら、タイミングが来るのを待つだけです。time_point 同士は単純に比較できます。


このような方法で実現できますが、現実的なプログラムでは、何もしないで延々と待ち続けるようなループを作ると、PC の処理性能を無駄に消費し続けることになり、作法としてはよくないとされます。より良い方法として、std::this_thread::sleep_for関数1を使う方法などがあります。

#include <chrono>
#include <iostream>
#include <thread>

int main()
{
    int wait_seconds {};
    std::cin >> wait_seconds;

    std::this_thread::sleep_for(std::chrono::seconds{wait_seconds});
    std::cout << "Hello.\n";
}

実行結果:

5  <-- 入力した値
Hello.  <-- 入力してから5秒後に出力

問題3 (調査★★) 🔗

お使いの処理系で、system_clock、steady_clock、high_resolution_clock について、分解能がどれだけあるか、時間が逆行するかどうかを調べてみてください。


system_clock、steady_clock、high_resolution_clock はいずれも、<chrono> で定義されているクロックを表すクラスです。いずれのクラスにも、分解能を表す period と、時間が逆行しないことを表す is_steady というメンバがあり、これがどのように定義されているかを確認します(本編解説)。

is_steady のほうは bool型の定数なので調べるのも、出力してみるのも簡単です。period のほうは std::ratio の typedef名で、std::ratio の2つのテンプレート実引数がどうなっているかを確認するか、計算した結果を出力します。std::ratio の静的メンバ変数num と den の値を使って、num / den を計算すれば秒単位で表した値を得られます。

#include <chrono>
#include <iostream>

template <typename Clock>
void print_resolution()
{
    std::cout << "resolution: " << static_cast<double>(Clock::period::num) / Clock::period::den << "\n";
}

template <typename Clock>
void print_is_steady()
{
    std::cout << "is_steady: " << std::boolalpha << Clock::is_steady << "\n";
}


int main()
{
    std::cout << "system_clock\n";
    print_resolution<std::chrono::system_clock>();
    print_is_steady<std::chrono::system_clock>();
    std::cout << "\n";

    std::cout << "steady_clock\n";
    print_resolution<std::chrono::steady_clock>();
    print_is_steady<std::chrono::steady_clock>();
    std::cout << "\n";

    std::cout << "high_resolution_clock\n";
    print_resolution<std::chrono::high_resolution_clock>();
    print_is_steady<std::chrono::high_resolution_clock>();
    std::cout << "\n";
}

実行結果(Visual Studio 2015):

system_clock
resolution: 1e-07
is_steady: false

steady_clock
resolution: 1e-09
is_steady: true

high_resolution_clock
resolution: 1e-09
is_steady: true

実行結果(MinGW-w64):

system_clock
resolution: 1e-009
is_steady: false

steady_clock
resolution: 1e-009
is_steady: true

high_resolution_clock
resolution: 1e-009
is_steady: false

出力結果の中で、分解能の 1e-07 は 1×10-7 のことです。つまり、10000000分の1秒=100ナノ秒ということになります。同様に 1e-009 は 1×10-9 のことなので 1ナノ秒です。


参考リンク 🔗



更新履歴 🔗




はてなブックマーク に保存 Pocket に保存 Facebook でシェア
X で ポストフォロー LINE で送る noteで書く
rss1.0 取得ボタン RSS 管理者情報 プライバシーポリシー
先頭へ戻る