C++1z オブジェクトをconstにするas_const関数

C++1zから<utility>ヘッダに、as_const()関数テンプレートが追加されます。

この関数は、引数として渡されたオブジェクトをconstにします。その際、オブジェクトのコピーは発生しません。また、この関数に右辺値は渡せません。

#include <iostream>
#include <utility>

int main()
{
    std::string a = "hello";
    const std::string& b = std::as_const(a); // 非constの左辺値をconstにする
    const std::string& c = std::as_const(b); // const左辺値をそのまま返す
//  const std::string& d = std::as_const(std::move(a)); // コンパイルエラー
}

この関数は、constと非constオーバーロードがあった場合に、const版を意図して呼び出すことを目的としているのだそうです。

言語組み込みのconst_cast命令はconst (とvolatile)の付け外しができますが、こちらはconstを付けることしかできません。

宣言

// <utility>
namespace std {
    template <class T>
    constexpr add_const_t<T>& as_const(T& t) noexcept;

    template <class T>
    void as_const(const T&&) = delete;
}

参照

お断り

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

C++1z shared_from_thisの指す先が書き換わらないことを規定

thisポインタをstd::shared_ptrとして取得するための機能として、std::enable_shared_from_this基本クラスとそのメンバ関数shared_from_this()があります。

std::enable_shared_from_thisから派生したクラスのオブジェクトをnewしてshared_ptrコンストラクタに渡すと、そのオブジェクトのthisshared_ptrとして取得できるようになります。

しかし、以下のように同じポインタから複数shared_ptrオブジェクトを作った場合に、shared_from_this()で返されるshared_ptrがどのオブジェクトを指すのか規定されていませんでした。

#include <memory>

using namespace std;

int main()
{
  struct X : public enable_shared_from_this<X> { };
  auto xraw = new X;
  shared_ptr<X> xp1(xraw);  // #1
  {
    shared_ptr<X> xp2(xraw, [](void*) { });  // #2
  }
  xraw->shared_from_this();  // #3
}

実装によっては、 #3 がxp1を指す場合もあれば、xp2を指す場合もありました。xp2を指す場合は、shared_from_this()の再束縛が行われることを意味します。

C++1zでは、shared_from_this()が再束縛されないことが既定されます(この場合はxp1を指し続けます)。また、thisを束縛する動作がスレッドセーフではないことも明記されます。

元々未規定だった動作ですが、xp2を指すことを期待していたプログラムは動作しなくなる可能性があるので注意してください。

また、再束縛されない仕様を明確にすることを主な目的として、std::enable_shared_from_thisクラスに、weak_ptrthisを返すweak_from_this()メンバ関数が追加されます。

参照

お断り

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

C++1z reference_wrapperがTriviallyCopyableであることを保証

C++11では、関数テンプレートに渡す引数を参照として渡すことを明示するstd::reference_wrapperクラスが導入されました。

これは通常、オブジェクトへの参照を受け取ってそのポインタを保持しておく、という簡単な方法で実装されます。このような実装になっているとreference_wrapperオブジェクトに対してmemcpy操作ができるのですが、そうなっていない実装が存在した(MSVC)ということで、実装を強制する保証が入ります。

標準C++にはTriviallyCopyableというコンセプトがあり、reference_wrapperクラスのオブジェクトはテンプレートパラメータに何が指定されたとしても、TriviallyCopyableコンセプトの要件を満たすことが実装に求められるようになります。

この保証があることで、Small Buffer Optimicationという最適化を、reference_wrapperのオブジェクトに対しても適用できるようになります。

template <class T, class Buffer>
struct use_small_buffer_optimization
    : std::integral_constant<bool,
        sizeof(T) <= sizeof(Buffer)
     && alignof(Buffer) % alignof(T) == 0
     && std::is_trivially_copyable<T>::value
     && std::is_copy_constructible<T>::value
> {};

参照

お断り

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

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に名前が変わり、従来の名前が非推奨になるので注意してください。