potisanのプログラミングメモ

趣味のプログラマーがプログラミング関係で気になったことや調べたことをいつでも忘れられるようにメモするブログです。はてなブログ無料版なので記事の上の方はたぶん広告です。記事中にも広告挿入されるみたいです。

C++20 コンセプト関係のメモ

C++20のコンセプト関係のメモです。

std::destructiblenoexcept(true)なデストラクタを持つクラスを含まない

std::destructiblenoexcept(false)なデストラクタを持つクラスを意味します。デストラクタはnoexcept(...)を省略すると例外的にnoexcept(true)が指定される特殊な関数なので通常は問題になりませんが、明示的にnoexcept(true)が指定された場合はデストラクト可能でもstd::destructiblefalseとなります。これはstd::destructiveが次の通り定義されることに由来します(参考:cppreference)。

template < class T >
concept destructible = std::is_nothrow_destructible_v<T>;

次のコードではclass_nothrow_destructクラスはnoexcept(true)なデストラクタを持ち、class_throw_destructクラスはnoexcept(false)なデストラクタを持ちます。std::destructiveは前者でtrue、後者でfalseとなります。

#include <concepts>
#include <iostream>

struct class_nothrow_destruct {
    ~class_nothrow_destruct() /*noexcept(true)*/ {
    }
};

struct class_throw_destruct {
    ~class_throw_destruct() noexcept(false) {
    }
};

int main()
{
    std::wcout.setf(std::ios_base::boolalpha);

    // std::destructible = std::is_nothrow_destructible_v
    std::wcout << std::destructible<class_nothrow_destruct> << std::endl; // true
    std::wcout << std::destructible<class_throw_destruct> << std::endl;   // false

    // std::is_destructible_v
    std::wcout << std::is_destructible_v<class_nothrow_destruct> << std::endl; // true
    std::wcout << std::is_destructible_v<class_throw_destruct> << std::endl;   // true

    return 0;
}

std::constructible_fromコンセプトはnoexcept(true)なデストラクタを持つクラスを含まない

std::constructible_fromコンセプトはクラスがある型の引数から構築可能であることを示しますが、クラスがnoexcept(true)なデストラクタを持つ場合はfalseとなります。これはstd::constructible_fromの定義が以下の通りであり、std::destructible<T>noexcept(false)なデストラクタしかtrueを返さないことに由来します(参考:cppreference)。

template < class T, class... Args >
concept constructible_from = std::destructible<T> && std::is_constructible_v<T, Args...>;

ある型が特定の型の引数から構築可能か調べる

std::constructible_fromを使えばある型が特定の型の引数から構築可能か調べることができます。ただし、noexcept(true)なデストラクタを持つクラスを含まないことに注意してください。

#include <concepts>
#include <iostream>
#include <string_view>

int main()
{
    std::wcout.setf(std::ios_base::boolalpha);

    // std::wstring_viewがstd::wstring、wchar_t[]等からそれぞれ構築可能か。
    std::wcout
        << std::constructible_from<std::wstring_view, std::wstring> << std::endl // true
        << std::constructible_from<std::wstring_view, wchar_t[]> << std::endl    // true
        << std::constructible_from<std::wstring_view, wchar_t*> << std::endl     // true
        << std::constructible_from<std::wstring_view, std::string> << std::endl  // false
        << std::constructible_from<std::wstring_view, int> << std::endl;         // false

    return 0;
}

std::ranges::sized_rangeを区切り文字列指定で標準出力へ出力する

#include <iostream>
#include <ranges>
#include <vector>

template <std::ranges::sized_range T>
void print(T r, std::wstring_view sep = L", ")
{
    std::ranges::subrange sr(r.begin(), std::prev(r.end(), 1));
    for (const auto& elem : sr) {
        std::wcout << elem << sep;
    }
    std::wcout << *std::next(r.begin(), r.size() - 1) << std::endl;
}

int main()
{
    print(std::vector{ 0, 1, 2, 3, 4 });
}