C++14標準ライブラリの小さな変更 その5
その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の時間指定待機に、例外の仕様を追加
std::future::wait_for()
std::future::wait_until()
std::shared_future::wait_for
std::shared_future::wait_until
時計クラス、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への操作が、例外を投げる可能性がある
OutputIterator要件のオブジェクトdestへの操作が、例外を投げる可能性がある
デフォルトコンストラクタに、例外を投げない規定が追加されました。
size()
メンバ関数に、例外を投げない規定が追加されました。
今日はここまで
もう少し続きます。
C++14標準ライブラリの小さな変更 その4
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に要件を追加
deque
のshrink_to_fit()
と同様の要件が、std::vector::reserve()
に追加されました。
型Tが*thisに対してムーブ挿入可能であること
vector::shrink_to_fitに要件と計算量の規定、例外が送出された際の挙動を追加
deque
のshrink_to_fit()
で追加された要件と計算量の規定に加え、vector::shrink_to_fit()
に、例外が送出された際の挙動が追加されました。
非コピー挿入可能な型Tのムーブコンストラクタが例外を送出した場合、 この関数は何もしない。
今日はこれまで
まだまだ続きます。
C++1zの実装状況をまとめはじめた
コンパイラの実装状況 - cpprefjp C++日本語リファレンス
Clang 3.6がリリースされたので、cpprefjpの「コンパイラの実装状況」ページにC++1zの状況をまとめました。
Clang 3.6のリリースノートは以下:
C++14標準ライブラリの小さな変更 その3
その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
その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の付け忘れ対応
std::integral_constant
クラスの、テンプレートパラメータの型T
への変換演算子に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>
ヘッダに追加された、future
/promise
関係のエラー値列挙型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_promise
をstd::error_code
に設定すると、エラー値なのに正常値と見なされてしまいます。
C++14のこの変更で、std::future_errc
のいずれの値も0以外になります。
今日はこれまで
さらに続きます。
C++14標準ライブラリの小さな変更 その1
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
が足されました。
size_t hash_code() const; // C++11 size_t hash_code() const noexcept; // C++14
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::async
かstd::launch::deferred
の、いずれかの実行ポリシーを指定します。
この実行ポリシーは列挙型の値なので、通常はこのどちらかしか渡されないのですが、整数値を無理やりstd::launch
型にキャストしてstd::async()
関数に指定される可能性があります。
C++14では、そのような値が渡された場合、「未定義動作になる」ということが規定されました。これまで未規定だったので、たいして変わらないです。
今日はこれまで
続きます。
Boost.Fusionのアルゴリズムとジェネリックラムダ
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
無事に動いた。
確認した環境:
イテレータの受け取り方
以下のコードは、コンパイルエラーになる:
template <class Iterator> Iterator f(const Iterator& it) { return it; } int main() { int ar[] = {1, 2, 3}; f(ar); // コンパイルエラー!呼び出し可能なf()のオーバーロードがない }
関数f()
はイテレータをconst
左辺値参照で受け取る。このようにした場合、テンプレートパラメータIterator
はint[3]
に推論される。C++の言語仕様として、組み込み配列はreturn
文で返せないので、コンパイルエラーになる(実際には、戻り値型のところでSFINAEが起きる)。
以下のように、イテレータをコピーで受け取ることで関数f()
を呼び出せるようになる:
template <class Iterator> Iterator f(Iterator it) { return it; } int main() { int ar[] = {1, 2, 3}; f(ar); // OK }
こうすることで、テンプレートパラメータIterator
はint*
に推論される。ポインタはreturn
文で返せるので、これは問題なく通る。
この変更が、C++14でのmake_move_iterator()
関数の仕様に適用された。
ちなみに、この推論ルールは、decay
(ディケイと読む)という名前で知られており、<type_traits>
ライブラリにはその推論ルールを適用するメタ関数が定義されている。
shared_timed_mutexで並行キューを実装した
Readers Writerロックで、並行キューを実装した。
値を返し、例外を投げないpop()
も実装しておいた。
実装: shand/concurrent/queue.hpp
インタフェース
namespace shand { template <class T> // Tは、例外を投げないムーブコンストラクタを持っていること class concurrent_queue { public: concurrent_queue() {} concurrent_queue(const concurrent_queue&) = delete; concurrent_queue& operator=(const concurrent_queue&) = delete; // write access void push(const T& x); void push(T&& x); boost::optional<T> pop() noexcept; void clear(); // read access std::size_t size() const; bool empty() const; }; } // namespace shand
サンプルコード
#include <iostream> #include <thread> #include <shand/concurrent/queue.hpp> void producer(shand::concurrent_queue<int>& que) { for (int i = 0; i < 100; ++i) { que.push(i); } } void consumer(shand::concurrent_queue<int>& que) { int i = 0; for (;;) { if (boost::optional<int> x = que.pop()) { std::cout << x.get() << std::endl; ++i; } if (i > 30) // てきとうなところで終了する return; } } int main() { shand::concurrent_queue<int> que; std::thread t1(producer, std::ref(que)); std::thread t2(consumer, std::ref(que)); t1.join(); t2.join(); }
出力:
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
値を返し、例外を投げないpop()の実装
値を返し、例外を投げないpop()
は、以下のようにして実装しています。
- 戻り値は、コピーではなくムーブで返す
- 空の場合を考慮して、
optional
で返す - クラスのテンプレートパラメータは、例外を投げないムーブコンストラクタを持っている型のみを受け付ける
optional
のムーブコンストラクタは、T
のムーブが例外を投げなければ、例外を投げない(と、1.57.0のドキュメントに書いていた)
テンプレートパラメータの要件部分:
template <class T> class concurrent_queue { static_assert( std::is_nothrow_move_constructible<T>::value, "Type T must be nothrow move constructible"); … };
pop()
の実装部分:
// que_はstd::deque<T> boost::optional<T> pop() noexcept { std::lock_guard<std::shared_timed_mutex> lock(mutex_); if (que_.empty()) return boost::none; T front = std::move(que_.front()); que_.pop_front(); return front; }