C++14標準ライブラリの小さな変更 その7

C++

その1その2その3その4その5その6に引き続き、C++14の標準ライブラリに入った小さな変更を紹介していきます。

condition_variableのwait系関数が例外を送出する仕様を変更

C++11までは、ミューテックスの取得関係で例外が送出される可能性があった。

C++14では、ミューテックスの取得関係では、wait系関数は例外を送出しない。その代わりにstd::terminate()関数を呼び出してプログラムが異常終了する。時計、時間演算関係で例外を送出する可能性がある。

tuple_sizeの特殊化が、必ずintegral_constantから派生するようになった

namespace std {
  template <class T> class tuple_size; // 先行宣言

  // C++11
  struct tuple_size<pair<T1, T2>> {
    static constexpr size_t value = 2;
  };

  // C++14
  struct tuple_size<pair<T1, T2>>
    : public integral_constant<size_t, 2> {};
}
namespace std {
  template <class T> class tuple_size; // 先行宣言

  // C++11
  template <class T, size_t N>
  struct tuple_size<array<T, N>> {
    static constexpr size_t value = N;
  };

  // C++14
  template <class T, size_t N>
  struct tuple_size<array<T, N>>
    : integral_constant<size_t, N> {};
}

is_null_pointer型特性を追加

ヌルポインタ型かを判定するis_null_pointer型特性が、<type_traits>に追加されました。

namespace std {
  template <class T> 
  struct is_null_pointer;
}

この型特性は、型Tstd::nullptr_tであればstd::true_typeから派生し、そうでなければstd::false_typeから派生します。

copy_ifに戻り値の仕様を追加

C++11で追加されたアルゴリズムstd::copy_if()ですが、戻り値の仕様が未規定だったので、C++14で以下の仕様が追加されました。

戻り値
コピー先の範囲の終端を返す。

mem_fnの不要なオーバーロードを削除

C++11:

namespace std {
  template<class R, class T> unspecified mem_fn(R T::*);
  template<class R, class T, class... Args>
  unspecified mem_fn(R (T::*)(Args...));
  template<class R, class T, class... Args>
  unspecified mem_fn(R (T::*)(Args...) const);
  template<class R, class T, class... Args>
  unspecified mem_fn(R (T::*)(Args...) volatile);
  template<class R, class T, class... Args>
  unspecified mem_fn(R (T::*)(Args...) const volatile);
  template<class R, class T, class... Args>
  unspecified mem_fn(R (T::*)(Args...) &);
  template<class R, class T, class... Args>
  unspecified mem_fn(R (T::*)(Args...) const &);
  template<class R, class T, class... Args>
  unspecified mem_fn(R (T::*)(Args...) volatile &);
  template<class R, class T, class... Args>
  unspecified mem_fn(R (T::*)(Args...) const volatile &);
  template<class R, class T, class... Args>
  unspecified mem_fn(R (T::*)(Args...) &&);
  template<class R, class T, class... Args>
  unspecified mem_fn(R (T::*)(Args...) const &&);
  template<class R, class T, class... Args>
  unspecified mem_fn(R (T::*)(Args...) volatile &&);
  template<class R, class T, class... Args>
  unspecified mem_fn(R (T::*)(Args...) const volatile &&);
}

C++14:

namespace std {
  template<class R, class T>
  unspecified mem_fn(R T::*);
}

単に冗長なオーバーロードでした。

functionに、異なるシグニチャの関数オブジェクトが代入された際の挙動を追加

以下のオーバーロードについて、

template <class F>
function(F f);

template <class F, class Alloc>
function(allocator_arg_t, const Alloc& alloc, F f);

C++11までは、型Fstd::functionのテンプレートパラメータと同じシグニチャを持つことを「要件」として定義していました。

C++14では、異なるシグニチャの関数オブジェクトが渡された場合、オーバーロード解決の候補から外れます(SFINAEになる)。

こちらもC++14では以下のオーバーロードについて、

template<class F>
function& operator=(F&& f);

異なるシグニチャの関数オブジェクトが渡された場合、オーバーロード解決の候補から外れます。

vector<bool>にemplaceとemplace_backを追加

単に書き忘れです。

cpprefjpにどう書こうか悩んだのですが、暫定的に、std::vectorの各メンバ関数のページに書きました。

template <class... Args>
iterator emplace(const_iterator position, Args&&... args);

template <class... Args>
iterator vector<bool>::emplace(const_iterator position, Args&&... args); // C++14
template <class... Args>
void emplace_back(Args&&... args);

template <class... Args>
void vector<bool>::emplace_back(Args&&... args); // C++14

今日はここまで

ひとまずネタが尽きたのですが、cpprefjp側の対応が進んだらもう少し書きます。

C++14標準ライブラリの小さな変更 その6

C++

その1その2その3その4その5に引き続き、C++14の標準ライブラリに入った小さな変更を紹介していきます。

unique_ptr::reset(nullptr_t)の仕様を追加

配列版std::unique_ptr<T[]>クラスの、nullptrを受け取るreset()メンバ関数の仕様が、これまで未規定でした。C++14で、以下の仕様が規定されました。

reset(pointer())と同等の効果を持つ。

つまり、p.reset()するのと同じです。

allocator_traits::max_sizeのパラメータにconstを追加

単なる付け忘れです。

static size_type max_size(Alloc& a);                // C++11
static size_type max_size(const Alloc& a) noexcept; // C++14

is_signedとis_unsignedの符号判定の条件式を修正

C++11のstd::is_signedでは、以下の条件式で、算術型であることとその符号あるなしを判定していました。

is_arithmetic<T>::value && T(-1) < T(0)

しかし、算術型以外が渡された場合、T(-1) < T(0)という式でテンプレートの置き換え失敗が発生してしまうので、C++14で以下のように修正されました。

is_arithmetic<T>::value == trueの場合、integral_constant<bool, T(-1) < T(0)>::valueの
結果を真偽の結果とする。そうでなければ偽の結果とする。

std::is_unsignedも同様です。

is_destructibleに、オブジェクト型以外が渡された場合の仕様を追加

std::is_destructibleは、C++11では以下のようにして破棄可能であると判定していました。

型Tが完全型で
template <class U>
struct test { U u; };
があるときに test<T>::~test() がdelete宣言されていなければ、
型Tは破棄可能であると判断される。

この仕様では、オブジェクト型以外に対しては、未規定となっています。

C++14で、以下のように修正されました。

実行時に評価されない文脈で、オブジェクト型Tに対する式std::declval<T&>().~T()が有効であれば破棄可能、
そうでなければ破棄できないと判断される。

以下、オブジェクト型に含まれない型の場合の判断について記載する:
・Tがvoidの場合は破棄できない
・Tが参照型の場合は破棄可能
・Tが関数型の場合は破棄できない

今日はここまで

まだ続きます。

C++14標準ライブラリの小さな変更 その5

C++

その1その2その3その4に引き続き、C++14の標準ライブラリに入った小さな変更を紹介していきます。

error_categoryにデフォルトコンストラクタを追加

std::error_categoryクラスのコンストラクタに、デフォルトコンストラクタを追加しました。

constexpr error_category() noexcept;            // (1) C++14
error_category(const error_category&) = delete; // (2)

単なる記載忘れです。

future/shared_futureの時間指定待機に、例外の仕様を追加

時計クラス、time_pointクラス、durationクラスの構築が例外を送出する場合、
この関数はそれらの例外を送出する。

promise::set_value_thread_at_exitに、例外の仕様を追加

std::promise::set_value_at_thread_exit()メンバ関数の以下のバージョンに、例外の仕様が追加されました。

void promise::set_value_at_thread_exit(const R& r);

R型のオブジェクトをコピーするために選択されたコンストラクタが、
あらゆる例外を送出する可能性がある
void promise::set_value_at_thread_exit(R&& r);

R型のオブジェクトをムーブするために選択されたコンストラクタが、
あらゆる例外を送出する可能性がある

durationのコンストラクタに、オーバーロード解決される条件を追加

単位変換のコンストラクタに、「単位変換の結果としてオーバーフローするような場合、オーバーロード解決から除外される」という条件が追加されました。

template <class Rep2, class Period2>
constexpr duration(const duration<Rep2, Period2>& d);

この関数は、以下の条件を満たす場合にオーバーロード解決される。
・C++11 : treat_as_floating_point<rep>::value == true
・C++14 : 単位変換の結果としてオーバーフローせず、treat_as_floating_point<rep>::value == true
・もしくは、treat_as_floating_point<rep>::value == falseかつratio_divide<Period2, period>::type::den == 1
・これらの要求は、整数ベースのduration型間での変換の際に、暗黙に切り捨て誤差が起きるのを防ぐ。
浮動小数点数型ベースの場合には、精度が下がれば小数点以下の数値になるだけなので問題ない。

seed_seqクラスのメンバ関数に、例外の仕様を追加

std::seed_seq::generate()メンバ関数に、例外の仕様が追加されました:

RandomAccessIterator要件のオブジェクトbegin、endへの操作が、例外を投げる可能性がある

param()メンバ関数に、例外の仕様が追加されました:

OutputIterator要件のオブジェクトdestへの操作が、例外を投げる可能性がある

デフォルトコンストラクタに、例外を投げない規定が追加されました。

size()メンバ関数に、例外を投げない規定が追加されました。

今日はここまで

もう少し続きます。

C++14標準ライブラリの小さな変更 その4

C++

cpprefjpのSEO関係の対応をしてたり、cpprefjpの数式をMathJaxに対応してたり、Boost 1.58.0のリリースノート翻訳をやっていたりしたので、こちらが滞っていました。

その1その2その3に引き続き、C++14の標準ライブラリに入った小さな変更を紹介していきます。

atomic_flag::clearの要件追加

std::atomic_flagクラスのclear()メンバ関数に渡すメモリオーダーに、「memory_order_consumeを指定してはならない」という要件が追加されました。

orderが以下のメモリオーダーではないこと:

・memory_order_consume (C++14)
・memory_order_acquire
・memory_order_acq_rel

deque::resizeとvector::resizeの内部的な挙動変更

C++11まで、std::deque::resize()の挙動は、以下のようなものでした:

if (sz > size())
  insert(end(), sz - size(), c);
else if (sz < size())
  erase(begin() + sz, end());

C++14からは、以下のようになりました:

・もしszが現在のコンテナのsize()より小さい場合、pop_back()関数をsize() - sz回呼ぶ
・もしszが現在のコンテナのsize()より大きい場合、sz -size()個だけオブジェクトcのコピーを追加する。

要件も少し緩くなっています。

この変更は、std::vector::resize()に対しても行われています。

deque::shrink_to_fitに要件と計算量の規定を追加

C++11から追加されたstd::deque::shrink_to_fit()メンバ関数に、以下の要件:

型Tが*thisに対してムーブ挿入可能であること

および、計算量の規定が追加されました。

最大で、要素数に対して線形時間

vector::reserveに要件を追加

dequeshrink_to_fit()と同様の要件が、std::vector::reserve()に追加されました。

型Tが*thisに対してムーブ挿入可能であること

vector::shrink_to_fitに要件と計算量の規定、例外が送出された際の挙動を追加

dequeshrink_to_fit()で追加された要件と計算量の規定に加え、vector::shrink_to_fit()に、例外が送出された際の挙動が追加されました。

非コピー挿入可能な型Tのムーブコンストラクタが例外を送出した場合、
この関数は何もしない。

今日はこれまで

まだまだ続きます。

C++1zの実装状況をまとめはじめた

C++

コンパイラの実装状況 - cpprefjp C++日本語リファレンス

Clang 3.6がリリースされたので、cpprefjpの「コンパイラの実装状況」ページにC++1zの状況をまとめました。

Clang 3.6のリリースノートは以下:

Clang 3.6 Release Notes — Clang 3.6 documentation

C++14標準ライブラリの小さな変更 その3

C++

その1その2に引き続き、C++14の標準ライブラリに入った小さな変更を紹介していきます。

細かいnoexceptの付け忘れ対応

C++11で<memory>ヘッダに追加されたアロケータの中間インタフェースstd::allocator_traitsクラスmax_size()静的メンバ関数に、noexceptが追加されました。

static size_type max_size(Alloc& a);                // C++11
static size_type max_size(const Alloc& a) noexcept; // C++14

ついでに、constの付け忘れにも対応。

weak_ptrのlockがスレッドセーフに

weak_ptrオブジェクトが監視しているshared_ptrオブジェクトを取得するlock()メンバ関数が、アトミックに実行されることが保証されました。

shared_ptr<T> lock() const noexcept;

戻り値:
expired() ? shared_ptr<T>() : shared_ptr<T>(*this)

監視しているshared_ptrオブジェクトが有効な状態なら、そのshared_ptrオブジェクトとリソースを共有するshared_ptrオブジェクトを作って返す。これによって、ロックしている間、shared_ptrオブジェクトの寿命が尽きないようにする。

監視しているshared_ptrオブジェクトが寿命切れ状態なら、空のshared_ptrオブジェクトを作って返す。

C++14 : 上記に相当することをアトミックに実行する。

文字列から数値に変換する関数のエラーに、ERANGEを考慮する

C++11からされたbasic_stringを整数もしくは浮動小数点数に変換する関数に、ERANGEエラーが考慮されるようになりました。

変更対象となる関数は以下です:

C++11では、「std::strtol()などの関数を使用した変換結果の値が、結果型で表現可能な範囲を超えていたらエラー」という仕様はありました。

C++14にはこれに加えて、「変換に使用する内部関数std::strtol()が範囲外エラーになったらエラーとする」という仕様が追加されました。

今日はこれまで

まだ続きます。

C++14標準ライブラリの小さな変更 その2

C++

その1に引き続き、C++14の標準ライブラリに入った、小さな変更を紹介していきます。

weak_ptrがムーブに対応した

C++14では、弱参照スマートポインタのstd::weak_ptrクラスに、以下のコンストラクタと、

weak_ptr(weak_ptr&& r) noexcept;

template <class Y>
weak_ptr(weak_ptr<Y>&& r) noexcept;

以下の代入演算子が追加されました。

weak_ptr& operator=(weak_ptr&& r) noexcept;

template <class Y>
weak_ptr& operator=(weak_ptr<Y>&& r) noexcept;

これまで、weak_ptrはコピーはできたので、既存コードに影響はありません。

細かいnoexceptの付け忘れ対応

constexpr operator value_type()          { return value; } // C++11
constexpr operator value_type() noexcept { return value; } // C++14
  • <ios>ヘッダの以下の関数に、noexceptが追加されました。

iostream_category()関数 : 入出力のエラーカテゴリオブジェクトを作って返す。

namespace std {
  const error_category& iostream_category();          // C++11
  const error_category& iostream_category() noexcept; // C++14
}

make_error_code()関数 : 入出力関係のエラーコードを作って返す。

namespace std {
  error_code make_error_code(io_errc e);          // C++11
  error_code make_error_code(io_errc e) noexcept; // C++14
}

make_error_condition()関数 : 入出力関係のエラーコードに紐づく情報を、作って返す。

namespace std {
  error_condition make_error_condition(io_errc e);          // C++11
  error_condition make_error_condition(io_errc e) noexcept; // C++14
}

メンバ関数版の組み込み配列用begin()end()に、noexceptが追加されました。

namespace std {
  template <class T, size_t N>
  T* begin(T (&array)[N]);                    // C++11

  template <class T, size_t N>
  constexpr T* begin(T (&array)[N]) noexcept; // C++14
}
namespace std {
  template <class T, size_t N>
  T* end(T (&array)[N]);                    // C++11

  template <class T, size_t N>
  constexpr T* end(T (&array)[N]) noexcept; // C++14
}

future_errcの開始値が0ではなくなった

C++11で<future>ヘッダに追加された、futurepromise関係のエラー値列挙型std::future_errcですが、列挙値が実装定義になりました。

C++11:

namespace std {
  // C++11
  enum class future_errc {
    broken_promise,
    future_already_retrieved,
    promise_already_satisfied,
    no_state
  };
}

C++14:

namespace std {
  enum class future_errc {
    broken_promise = implementation-defined,
    future_already_retrieved = implementation-defined,
    promise_already_satisfied = implementation-defined,
    no_state = implementation-defined
  };
}

この変更は、std::future_errcの値をstd::error_codeオブジェクトに入れたときに、開始値が0だと困るからです。

始値broken_promiseは、正常値ではなくエラー値です。これが0だと困るのは、std::error_codeクラスのoperator bool()が、以下の仕様になっていることです。

explicit operator bool() const noexcept;

戻り値:
return value() != 0

つまり、0を正常値、0以外をエラー値としています。

そのため、C++11のbroken_promisestd::error_codeに設定すると、エラー値なのに正常値と見なされてしまいます。

C++14のこの変更で、std::future_errcのいずれの値も0以外になります。

今日はこれまで

さらに続きます。

C++14標準ライブラリの小さな変更 その1

C++

C++日本語リファレンスとサイトcpprefjpで、C++14対応を進めているので、このブログですでに紹介した大きな機能以外の、小さな変更点を紹介していきます。

コンテナに挿入するイテレータアダプタに、addressofを使うようになった

C++11から、std::addressof()という関数が<memory>ヘッダに追加されました。

この関数は、オブジェクトのアドレスを取得するoperator&()オーバーロードされていたとしても、そのオブジェクトのアドレスを取得できるようにする関数です。Boostから導入されたもの。

標準ライブラリのテンプレート内で、オブジェクトのポインタを取得する箇所は、このaddressof()関数を使用するようになりました。

back_insert_iteratorコンストラクタは、以下のように仕様が変わっています。

explicit back_insert_iterator(Container& x);

xへのポインタをメンバ変数containerに保持する。

C++11まで : xへのポインタは、&xで取得する
C++14以降 : xへのポインタは、std::addressof(x)で取得する

細かいnoexceptの付け忘れ対応

std::type_indexクラスの以下のメンバ関数に、付け忘れていたnoexceptが足されました。

hash_code()メンバ関数

size_t hash_code() const;          // C++11
size_t hash_code() const noexcept; // C++14

name()メンバ関数

const char* name() const;          // C++11
const char* name() const noexcept; // C++14

ほかにもいろいろあるので、追々紹介していきます。

async関数に、不正なポリシーが指定された場合の挙動が追加された

namespace std {
  template <class F, class... Args>
  future<typename result_of<F(Args...)>::type>
    async(launch policy, F&& f, Args&&... args);
}

C++11から<future>ヘッダに追加された、簡単な非同期処理をするためのasync()関数には、std::launch::asyncstd::launch::deferredの、いずれかの実行ポリシーを指定します。

この実行ポリシーは列挙型の値なので、通常はこのどちらかしか渡されないのですが、整数値を無理やりstd::launch型にキャストしてstd::async()関数に指定される可能性があります。

C++14では、そのような値が渡された場合、「未定義動作になる」ということが規定されました。これまで未規定だったので、たいして変わらないです。

今日はこれまで

続きます。

Boost.Fusionのアルゴリズムとジェネリックラムダ

C++

C++14で導入されたジェネリックラムダはBoost.Fusionのためにあるようなものですが、この組み合わせをまだ動かしていなかったので、検証。

#include <iostream>
#include <boost/fusion/include/make_vector.hpp>
#include <boost/fusion/include/for_each.hpp>

int main()
{
    namespace fusion = boost::fusion;
    fusion::for_each(fusion::make_vector(1, "hello", 3.14), [](const auto& x) {
        std::cout << x << std::endl;
    });
}

出力:

1
hello
3.14

無事に動いた。

確認した環境:

  • Boost 1.57.0
  • GCC 4.9 (-std=c++1yオプション, 5.0からは-std=c++14オプション)
  • Clang 3.4 (-std=c++1yオプション, 3.5からは-std=c++14オプション)

イテレータの受け取り方

C++

以下のコードは、コンパイルエラーになる:

template <class Iterator>
Iterator f(const Iterator& it)
{
    return it;
}

int main()
{
    int ar[] = {1, 2, 3};
    f(ar); // コンパイルエラー!呼び出し可能なf()のオーバーロードがない
}

関数f()イテレータconst左辺値参照で受け取る。このようにした場合、テンプレートパラメータIteratorint[3]に推論される。C++の言語仕様として、組み込み配列はreturn文で返せないので、コンパイルエラーになる(実際には、戻り値型のところでSFINAEが起きる)。

以下のように、イテレータをコピーで受け取ることで関数f()を呼び出せるようになる:

template <class Iterator>
Iterator f(Iterator it)
{
    return it;
}

int main()
{
    int ar[] = {1, 2, 3};
    f(ar); // OK
}

こうすることで、テンプレートパラメータIteratorint*に推論される。ポインタはreturn文で返せるので、これは問題なく通る。

この変更が、C++14でのmake_move_iterator()関数の仕様に適用された。

ちなみに、この推論ルールは、decay(ディケイと読む)という名前で知られており、<type_traits>ライブラリにはその推論ルールを適用するメタ関数が定義されている。