C++1z コンテナの要素情報にアクセスする非メンバ関数

C++11では、範囲for文を拡張することを目的として、std::begin()std::end()という非メンバ関数が導入されました。

std::vector<T> v;

// 先頭要素を指すイテレータ、終端イテレータを非メンバ関数で取り出す
decltype(v)::iterator first = std::begin(v);
decltype(v)::iterator last = std::end(v);

これらの非メンバ関数は組み込み配列版に対するオーバーロードが用意されていることもあり、範囲for文を拡張する以外の目的でも便利に使われはじめ、C++14では範囲for文とは直接関係のないconst_iterator版のstd::cbegin()std::cend()、reverse_iterator版のstd::rbegin()std::rend()などが導入されました。

C++1zでは、組み込み配列とコンテナを共通インタフェースで扱えることなどを目的として、以下の非メンバ関数が導入されます:

  • std::empty() : コンテナが空か判定する
  • std::size() : コンテナの要素数を取得する
  • std::data() : コンテナの生データ(ポインタ)を取得する

これらのうち、std::size()はとくに組み込み配列の要素数を取得するために、便利に使えるでしょう。

#include <iterator>
#include <cstddef>

int main()
{
    int ar[] = {3, 1, 4};
    constexpr std::size_t size = std::size(ar);
    static_assert(size == 3);
}

この関数を使えば、#define ARRAYSIZE(a) (sizeof(a)/sizeof(*(a)))のようなマクロは必要なくなります。

その他の要点は、以下の通りです:

  • これらの関数は、<iterator>ヘッダで定義される
  • これらの関数は、constexpr関数として定義される
  • これらの関数のうちコンテナ版は、戻り値の型がコンテナの定義に依存する
    • たとえば、std::empty(c)は直接bool型を返すよう定義されているのではなく、c.empty()の戻り値の型を返すように定義される
  • これらの関数は、関数テンプレートのコンテナ版、std::initializer_list版、組み込み配列版のオーバーロードが標準ライブラリで定義される
  • これらの関数のうちstd::data()だけは、const左辺値参照版と非const左辺値参照版のオーバーロードが定義される。それ以外はconst左辺値参照版のみで、右辺値参照版はない。

宣言

// <iterator>
namespace std {
    // 24.8, container access:
    template <class C> constexpr auto size(const C& c) -> decltype(c.size());
    template <class T, size_t N> constexpr size_t size(const T (&array)[N]) noexcept;
    template <class C> constexpr auto empty(const C& c) -> decltype(c.empty());
    template <class T, size_t N> constexpr bool empty(const T (&array)[N]) noexcept;
    template <class E> constexpr bool empty(initializer_list<E> il) noexcept;
    template <class C> constexpr auto data(C& c) -> decltype(c.data());
    template <class C> constexpr auto data(const C& c) -> decltype(c.data());
    template <class T, size_t N> constexpr T* data(T (&array)[N]) noexcept;
    template <class E> constexpr const E* data(initializer_list<E> il) noexcept;
}

参照

お断り

この記事の内容は、C++1zが正式リリースされる際には変更される可能性があります。正式リリース後には、C++日本語リファレンスサイトcpprefjpの以下の階層の下に解説ページを用意する予定です。

C++1z 値を返す型特性クラスのconstexpr変数テンプレート版

C++14では、f<Arg>::typeのような型を返す型特性クラスに対して、f_t<Arg>のように_tをクラス名に付けて::typeを省略するエイリアステンプレート版が追加されました。

C++1zでは、f<Arg>::valueのような値を返す型特性クラスに対して、f_v<Arg>のように_vをクラス名に付けて::valueを省略する変数テンプレート版が追加されます。

ttypevvalueを意味します。

#include <type_traits>

int main()
{
    // 値を返す型特性クラスの、通常の使い方
    static_assert(std::is_void<void>::value); // 明示的に結果を取り出す
    static_assert(std::is_void<void>()());    // 関数呼び出し演算子
    static_assert(std::is_void<void>());      // boolへの型変換演算子

    // C++1zで追加される変数テンプレート版
    static_assert(std::is_void_v<void>);
}

変数テンプレートに対応する型特性クラスの一覧は、参照先の提案文書で確認してください。

参照

お断り

この記事の内容は、C++1zが正式リリースされる際には変更される可能性があります。正式リリース後には、C++日本語リファレンスサイトcpprefjpの以下の階層の下に解説ページを用意する予定です。

C++1z shared_mutex

C++14では、Readers-writerパターンという、読み込み操作が多い状況で使うミューテックスとして、std::shared_timed_mutexクラスが導入されました。

C++1zでは、タイムアウト付きロック取得機能がないクラスとして、shared_mutexクラスが導入されます。

// <shared_mutex>
namespace std {
    class shared_mutex {
    public:
        shared_mutex();
        ~shared_mutex();

        shared_mutex(const shared_mutex&) = delete;
        shared_mutex& operator=(const shared_mutex&) = delete;

        // 書き込み用のロック
        void lock();
        bool try_lock();
        void unlock();

        // 読み込み用のロック
        void lock_shared();
        bool try_lock_shared();
        void unlock_shared();

        // OSが持っているミューテックスの機能を直接使うときのためのネイティブハンドル
        typedef implementation-defined native_handle_type;
        native_handle_type native_handle();
    };
}

経緯として、C++14ではshared_mutex入る予定でしたが、その定義がタイムアウト付きロック取得の機能を持っているものであったため、shared_timed_mutexという名前で導入されていました。その後、タイムアウト付きロック取得がないshared_mutexも改めて導入しようという話になっていましたが、C++14には間に合わず、C++1zに繰り越されることになっていました。

参照

お断り

この記事の内容は、C++1zが正式リリースされる際には変更される可能性があります。正式リリース後には、C++日本語リファレンスサイトcpprefjpの以下の階層の下に解説ページを用意する予定です。

C++1z スコープ付きロックの可変引数版

関数内でロックを取得したミューテックスを、関数の終わりで確実にロックを手放すstd::lock_guard<MutexType>クラスがありますが、C++1zから可変個のミューテックス型およびミューテックスオブジェクトをとるstd::scoped_lockクラスが追加されます。

std::mutex m1;
std::timed_mutex m2;
std::shared_timed_mutex m3;
void f()
{
    std::scoped_lock<std::mutex, std::timed_mutex, std::shared_timed_mutex> lk
    {
        m1, m2, m3
    };

    // ...共有リソースに対する操作...
} // ここでm1, m2, m3のunlock()メンバ関数が呼ばれる

std::scoped_lockのテンプレート引数は、1つ以上指定する必要があります。

ロック取得済みのミューテックスオブジェクトであることを指示するstd::adopt_lockは、std::scoped_lockのコンストラクタの最後の引数に指定します。

std::lock(m1, m2, m3);
std::scoped_lock<std::mutex, std::timed_mutex, std::shared_timed_mutex> lk
{
    m1, m2, m3, std::adopt_lock
};

ミューテックスオブジェクトに個別で「これはロック取得済み、これはロック取得していない」というような指定はできません。

std::shared_lockクラスの方は、可変引数化の対応はありません。

参照

お断り

この記事の内容は、C++1zが正式リリースされる際には変更される可能性があります。正式リリース後には、C++日本語リファレンスサイトcpprefjpの以下の階層の下に解説ページを用意する予定です。

記事更新

  • 2017年3月のドラフト仕様更新により、std::lock_guardの可変引数化がとりやめとなり、可変引数をとるstd::scoped_lockクラスを新設することになりました

C++1z コンパイル時条件の論理演算

C++11時点で、コンパイル時条件をハンドリングするメタ関数として、以下があります:

C++1zからは、コンパイル時条件の論理演算を行う3つのメタ関数と、その変数テンプレート版が追加されます。

// <type_traits>
namespace std {
    // 論理積 (AND)
    template <class... B> struct conjunction;

    // 論理和 (OR)
    template <class... B> struct disjunction;

    // 否定 (NOT)
    template <class B> struct negation;

    // 変数テンプレート版
    template <class... B>
        constexpr bool conjunction_v = conjunction<B...>::value;
    template <class... B>
        constexpr bool disjunction_v = disjunction<B...>::value;
    template <class B>
        constexpr bool negation_v = negation<B>::value;
}

conjunctionコンパイル時条件の論理積 (AND) を求めるメタ関数です。boost::mpl::and_相当。integral_constant<boolに変換可能な型, B>のシーケンスを受け取って、それらを論理積した結果のbool_constantを継承してconjunctionが定義されます。

#include <type_traits>

int main()
{
    // 全ての条件がtrueなら結果がtrueとなる
    constexpr bool result = std::conjunction<
        std::true_type,
        std::is_void<void>,
        std::is_integral<int>
    >::value;
    static_assert(result);

    // 変数テンプレート版
    constexpr bool result2 = std::conjunction_v<
        std::true_type,
        std::is_void<void>,
        std::is_integral<int>
    >;
    static_assert(result2);
}

disjunctionコンパイル時条件の論理和 (OR) を求めるメタ関数です。boost::mpl::or_相当。integral_constant<boolに変換可能な型, B>のシーケンスを受け取って、それらを論理和した結果のbool_constantを継承してdisjunctionが定義されます。

#include <type_traits>

int main()
{
    // いずれかの条件がtrueなら結果がtrueとなる
    constexpr bool result = std::disjunction<
        std::false_type,
        std::is_void<char>,
        std::is_integral<int>
    >::value;
    static_assert(result);

    // 変数テンプレート版
    constexpr bool result2 = std::disjunction_v<
        std::false_type,
        std::is_void<char>,
        std::is_integral<int>
    >;
    static_assert(result2);
}

negation は、コンパイル時条件を否定 (NOT) するメタ関数です。integral_constant<boolに変換可能な型, B>を受け取って、それを否定したbool_constant<!B>を継承してnegationが定義されます。

#include <type_traits>

int main()
{
    // 条件を否定する
    constexpr bool result = std::negation<std::true_type>::value;
    static_assert(!result);

    // 変数テンプレート版
    constexpr bool result2 = std::negation_v<std::false_type>;
    static_assert(result2);
}

参照

お断り

この記事の内容は、C++1zが正式リリースされる際には変更される可能性があります。正式リリース後には、C++日本語リファレンスサイトcpprefjpの以下の階層の下に解説ページを用意する予定です。

C++1z bool_constant

C++1zから、標準ライブラリの<type_traits>に、integral_constantの第1テンプレート引数をboolにした別名のbool_constantが追加されます。

それにともない、true_typefalse_typeの定義が、integral_constantを使ったものからbool_constantを使ったものに変更されます。

namespace std {
    template <class T, T v> struct integral_constant;

    template <bool B>
    using bool_constant = integral_constant<bool, B>;

    typedef bool_constant<true> true_type;
    typedef bool_constant<false> false_type;
}

true_typefalse_typeはよく使うので、それらを受け取るテンプレートで部分特殊化や関数テンプレートの推論をする場合にbool_constantがあると、integral_constant<bool, B>のようなお決まりコードが短く済むようになります。

参照

お断り

この記事の内容は、C++1zが正式リリースされる際には変更される可能性があります。正式リリース後には、C++日本語リファレンスサイトcpprefjpの以下の階層の下に解説ページを用意する予定です。

Boost 1.61.0がリリースされました

Boost 1.61.0がリリースされました。

リリースノートはいつものように、boostjpサイトで翻訳 + 情報補完したものを公開しています。今回は新ライブラリが4つもあります。

GPGPUを扱うBoost.Compute、DLLとDSOを扱うBoost.DLL、現代的なメタプログラミングを行うHana、コンパイル時に構文解析を行うMetaparseが新ライブラリです。

boost::optionalT&で特殊化され、単なるポインタで実装される最適化が入りました。

文字配列をstringクラス風に扱うboost::string_refboost::string_viewに名前が変わり、従来の名前が非推奨になるので注意してください。

C++1z 関数が呼び出し可能かを判定する型特性

C++1zからは、関数が呼び出せるかどうかをコンパイル時に判定する型特性として、is_callableis_nothrow_callableの2つが<type_traits>に追加されます。

// <type_traits>
namespace std {
    template <class Fn, class... ArgTypes> struct is_invocable;
    template <class R, class Fn, class... ArgTypes> struct is_invocable_r;

    template <class Fn, class... ArgTypes> struct is_nothrow_invocable;
    template <class R, class Fn, class... ArgTypes> struct is_nothrow_invocable_r;

    // 変数テンプレート版
    template <class Fn, class... ArgTypes> inline constexpr bool is_invocable_v
      = is_invocable<Fn, ArgTypes...>::value;
    template <class R, class Fn, class... ArgTypes> inline constexpr bool is_invocable_r_v
      = is_invocable_r<R, Fn, ArgTypes...>::value;
    template <class Fn, class... ArgTypes> inline constexpr bool is_nothrow_invocable_v
      = is_nothrow_invocable<Fn, ArgTypes...>::value;
    template <class R, class Fn, class... ArgTypes> inline constexpr bool is_nothrow_invocable_r_v
      = is_nothrow_invocable_r<R, Fn, ArgTypes...>::value;
}

is_invocableは、関数を呼び出せる場合にはtrue_type、呼び出せない場合にはfalse_typeから派生します。is_invocable_rは、戻り値型もセットで関数が呼び出せるかを判定します。

is_nothrow_invocableは、関数が例外を投げずに(noexcept(true))呼び出せる場合にはtrue_type、そうでない場合にはfalse_typeから派生します。

これらの型特性はテンプレートパラメータとして、以下をとります:

  1. 関数のシグニチャ(関数の型と引数の型リストを合わせたもの)
  2. 戻り値の型
#include <type_traits>

struct F {
    int operator()(int, double) { return 1; }
};

struct G {
    int operator()(int, double) noexcept { return 2; }
};

int main()
{
    static_assert(std::is_invocable<F, int, double>::value);
    static_assert(std::is_invocable<G, int, double>::value);

    // Gの関数呼び出し演算子はnoexceptなので、trueとなる
    static_assert(std::is_nothrow_invocable<F, int, double>::value == false);
    static_assert(std::is_nothrow_invocable<G, int, double>::value);

    // 変数テンプレート版は::valueを指定する必要がない
    static_assert(std::is_invocable_v<F, int, double>);
}

参照

お断り

この記事の内容は、C++1zが正式リリースされる際には変更される可能性があります。正式リリース後には、C++日本語リファレンスサイトcpprefjpの以下の階層の下に解説ページを用意する予定です。

C++1z constexprラムダ

C++1zからは、ラムダ式をconstexprとして定義・実行できるようになります。

ラムダ式をconstexprにするには、これまでmutableを付けていたところに、constexprを付けます。

int main()
{
    auto add = [](int x) constexpr { return x + 1; };
    static_assert(add(1) == 2);
}
  • ラムダ式には、constexprとmutableを両方、順不同で指定できます
  • ラムダ式にconstexprを指定した場合、ラムダ式によって生成される関数オブジェクトの関数呼び出し演算子がconstexprになります
  • constexprラムダは、キャプチャを含められます
  • ジェネリックラムダに対してもconstexpr指定ができます
  • ラムダ式によって生成される関数オブジェクトであるクロージャオブジェクトも、constexprになれるリテラル型に分類されるようになります

参照

お断り

この記事の内容は、C++1zが正式リリースされる際には変更される可能性があります。正式リリース後には、C++日本語リファレンスサイトcpprefjpの以下の階層の下に解説ページを用意する予定です。

C++1z 範囲for文のためのbegin()とend()関数が、異なる型を返すことを許可

C++11時点での範囲for文は、begin()関数とend()関数が同じ型のイテレータオブジェクトを返すことを要求していました。

しかし、end()の方のイテレータは特殊な型にしたいことがたまにあります。ストリームイテレータを作ろうとするときとかもそうだと思いますが、「終端」という意味だけを持たせるなら、終端以外の情報を終端イテレータに持たせる必要がないので、別の型にしたかったりします。

そのような状況のために、C++1zからは、begin()end()が異なる型のイテレータオブジェクトを返すことが許可されます。

C++11まで以下のようになっていた範囲for文の効果は、

{
    auto && __range = for-range-initializer;
    for ( auto __begin = begin-expr,
               __end = end-expr;
          __begin != __end;
          ++__begin ) {
        for-range-declaration = *__begin;
        statement
    }
}

C++1zでは以下のようになります:

{
    auto && __range = for-range-initializer;
    auto __begin = begin-expr;
    auto __end = end-expr;
    for ( ; __begin != __end; ++__begin ) {
        for-range-declaration = *__begin;
        statement
    }
}

参照

お断り

この記事の内容は、C++1zが正式リリースされる際には変更される可能性があります。正式リリース後には、C++日本語リファレンスサイトcpprefjpの以下の階層の下に解説ページを用意する予定です。