C++20:ライブラリ

前回の記事「C++20:コア言語」では、C++20 コア言語の新機能を紹介しました。今日も引き続き、C++20 ライブラリの概要について説明します。

画像は今日の私の計画を示しています。

ライブラリ

カレンダーとタイムゾーン

C++11/14 の chrono ライブラリは、カレンダーとタイムゾーン機能で拡張されました。 Chrono ライブラリをご存じない場合は、私の投稿を読んでください。

カレンダー

カレンダー:年、月、曜日、月の n 番目の曜日を表す型で構成されます。これらの基本型は、年_月、年_月_日、年_月_日_最後、年_月_週日、年_月_週日_最後などの複合型に組み合わせることができます。演算子「/」は、時間ポイントを便利に指定するためにオーバーロードされています。さらに、C++20 の新しいリテラルを取得します:d は日、y は年です。

タイムゾーン

時間ポイントは、さまざまな特定のタイム ゾーンで表示できます。

クロノ ライブラリが拡張されているため、次のユース ケースを簡単に実装できます。

  • さまざまな形式で日付を表す
auto d1 = 2019y/oct/28;
auto d2 = 28d/oct/2019;
auto d3 = oct/28/2019; 

  • 月の最終日を取得する
  • 2 つの日付の間の日数を取得する
  • さまざまなタイムゾーンでの現在時刻の表示

これらの機能を試してみたい場合は、GitHub の Howard Hinnards 実装を使用してください。カレンダーとタイムゾーンの提案の作成者である Howard Hinnard も、Wandbox でそのためのプレイグラウンドを作成しました。

#include "date.h"
#include <iostream>

int
main()
{
 using namespace date;
 using namespace std::chrono;
 auto now = system_clock::now();
 std::cout << "The current time is " << now << " UTC\n";
 auto current_year = year_month_day{floor<days>(now)}.year();
 std::cout << "The current year is " << current_year << '\n';
 auto h = floor<hours>(now) - sys_days{jan/1/current_year};
 std::cout << "It has been " << h << " since New Years!\n";
}

もちろん、C++20 は日付名前空間の代わりに std::chrono 名前空間を使用します。プログラムの出力は次のとおりです。

std::span

std::span は、オブジェクトの連続したシーケンスを参照できるオブジェクトを表します。ビューとも呼ばれる std::span は決して所有者ではありません。この連続したメモリは、配列、サイズのあるポインタ、または std::vector にすることができます。典型的な実装では、最初の要素へのポインタとサイズが必要です。 std::span を持つ主な理由は、関数に渡された場合、プレーン配列がポインターに減衰することです。したがって、サイズが失われます。標準: :span は、プレーン配列または std::vector のサイズを自動的に推測します。ポインターを使用して std::span を初期化する場合、コンストラクターのサイズを指定する必要があります。

template <typename T>
void copy_n(const T* p, T* q, int n){}

template <typename T>
void copy(std::span<const T> src, std::span<T> des){}

int main(){
 
 int arr1[] = {1, 2, 3};
 int arr2[] = {3, 4, 5};
 
 copy_n(arr1, arr2, 3); // (1)
 copy(arr1, arr2); // (2)
 
}

関数 copy_n (1) とは対照的に、copy (2) は要素数を必要としません。したがって、エラーの一般的な原因は std::span でなくなります。

constexpr コンテナ

C++ はますます constexpr になります。たとえば、標準テンプレート ライブラリの多くのアルゴリズムは、C++20 で constexpr オーバーロードを取得します。関数または関数テンプレートの constexpr は、コンパイル時に実行される可能性があることを意味します。問題は、コンパイル時にどのコンテナーを使用できるかということです。 C++20 では、答えは std::string と std::vector です。

C++20 より前では、constexpr の評価で両方を使用することはできませんでした。これは、3 つの制限的な側面があったためです。

<オール>
  • デストラクタを constexpr にすることはできません。
  • 動的メモリ割り当て/割り当て解除が利用できませんでした。
  • placement-new を使用したインプレース構築は利用できませんでした。
  • これらの制限的な側面は解決されました。

    ポイント 3 は、placement-new について述べていますが、これはまったく知られていません。 Placement-new は、事前に予約されたメモリ領域でオブジェクトをインスタンス化するためによく使用されます。さらに、placement-new をグローバルに、またはデータ型に対してオーバーロードできます。

    char* memory = new char[sizeof(Account)]; // allocate memory
    Account* account = new(memory) Account; // construct in-place
    account->~Account(); // destruct
    delete [] memory; // free memory
    

    ここでは、placement-new を使用する手順を示します。最初の行は、アカウントにメモリを割り当てます。これは、2 行目でアカウントをインプレースで構築するために使用されます。確かに、式 account->~Account() は奇妙に見えます。この式は、デストラクタを明示的に呼び出す必要があるまれなケースの 1 つです。最後に、最後の行でメモリを解放します。

    constexpr Containers の詳細についてはこれ以上説明しません。興味がある場合は、提案 784R1 をお読みください。

    std::format

    cppreference.com/ には、新しいフォーマット ライブラリの簡潔な説明があります。ユーザー定義型のオーバーロードされた挿入演算子などのインフラストラクチャ。".この簡潔な説明には、簡単な例が含まれています:

    std::string message = std::format("The answer is {}.", 42);
    

    おそらく、これは Python のフォーマット文字列を思い起こさせます。あなたが正しいです。 GitHub で利用可能な std::format の実装が既にあります:fmt.上記の実装からのいくつかの例を次に示します。 std の代わりに、名前空間 fmt を使用します。

    • 位置引数のフォーマットと使用
    std::string s = fmt::format("I'd rather be {1} than {0}.", "right", "happy");
    // s == "I'd rather be happy than right."
    

    • 安全な方法で整数を文字列に変換します
    fmt::memory_buffer buf;
    format_to(buf, "{}", 42); // replaces itoa(42, buffer, 10)
    format_to(buf, "{:x}", 42); // replaces itoa(42, buffer, 16)
    // access the string with to_string(buf) or buf.data()
    

    • ユーザー定義型の書式設定
    struct date {
     int year, month, day;
    };
    
    template <>
    struct fmt::formatter<date> {
     template <typename ParseContext>
     constexpr auto parse(ParseContext &ctx) { return ctx.begin(); }
    
     template <typename FormatContext>
     auto format(const date &d, FormatContext &ctx) {
     return format_to(ctx.out(), "{}-{}-{}", d.year, d.month, d.day);
     }
    };
    
    std::string s = fmt::format("The date is {}", date{2012, 12, 9});
    // s == "The date is 2012-12-9"
    

    次は?

    約束どおり、今後のライブラリへの投稿でさらに深く掘り下げます。しかしその前に、C++20 のハイレベルな概要を終わらせる必要があります。次の投稿は同時実行機能についてです。