Range を使おう
これは C++11 Advent Calendar 2011 の参加記事 (15日目) です.
この記事では「Iterator ではなく Range を使おう」という話をします.既に何度も言われている話ではありますが,私も一度書いてみたかったのでこの機会に書きます.でもそんなに新しいことは言わないと思います.
話の流れとしては,従来の Iterator にはこんな欠点が → それ Range ならうまく書けるよ → さらに Range 使ってこんなこともできるよ → C++11 での Range という感じになります.
従来の Iterator の問題点
Iterator は STL の主要なコンセプトで,Container と Algorithm を橋渡しする役割を持っています.
std::vector<int> v = { 8, 4, 3, 7, 6, 5, 2, 1 }; std::sort(v.begin(), v.end());
この例で,std::vector<int> と std::sort は互いを意識しておらず,Iterator を介してのみつながっています.Iterator は Container と Algorithm の結合度を下げ,それらが独立に実装されることを可能にしているわけです.
このように Iterator は素晴らしいものですが,問題点もあります.ここでは Iterator の問題点を2つ挙げてみます.
1つは,分かりにくいという点です.上の例について言えば,ソートは1つの範囲に対してなされるものであって,"Iterator" 2つを引数に取るインターフェースは非直観的だということです.また,呼び出すのも面倒です.
もう1つは,危険だという点です.上の例にちょっと手を加えてみましょう.
std::vector<int> v = { 8, 4, 3, 7, 6, 5, 2, 1 }, w; std::sort(v.begin(), w.end());
作為的すぎる気がしますが,もう少し複雑なコードの中ではありうるミスです.コンパイルは警告もなく通り,実行すると良くてクラッシュ,悪くて広範囲のデータを破壊して訳の分からないことになります.
そして,これに std::sort の側で対処することはできません.なぜなら,2つの Iterator が同じ範囲の要素を指していること,言い換えれば2つの Iterator を比較してよいことを知る一般的な方法はないからです *1.
これらの問題点をうまく解決してくれるのが Range です.
Range を使う
Range は従来の Container を一般化したコンセプトで,文字通り1つの範囲を表します.全ての Container および配列が Range であるほか,Boost.Range などのライブラリで多くの Range が提供されています.基本的に,Range は Range Algorithm と呼ばれるアルゴリズムに渡して使います.
では上の例を Range で書き直して,問題点が解消されていることを確認しましょう.std::vector<int> は既に Range なので,std::sort を Range Algorithm である boost::sort で置き換えます.
std::vector<int> v = { 8, 4, 3, 7, 6, 5, 2, 1 }; boost::sort(v);
とても直観的なコードになりました.また,誤って複数の範囲を渡してしまうこともありません.Range が Iterator より良い選択であることが分かると思います.
もっと Range を使う
Boost.Range には STL のものと同名の Range Algorithm が多数あり,上の boost::sort もその1つです.基本的には,Iterator 2つを取るところが Range 1つを取るように変わっていると考えて構いません.
void disp(int i) { std::cout << i << '\n'; } std::vector<int> const v = { 1, 2, 3, 4, 5 }; // もう古い! // std::for_each(v.begin(), v.end(), &disp); boost::for_each(v, &disp);
また先述の通り,配列も Range として使えます.
int const a[] = { 1, 2, 3, 4, 5 }; boost::for_each(a, &disp);
これだけでも十分 Range の便利さが分かると思いますが,Range の威力は,それを最大限に活用したライブラリがあることでより一層増します.そのようなライブラリとして,ここでは PStade.Oven を紹介しましょう.次の例は,Range 中のユニークな要素の数を求めるものです.
using namespace pstade::oven; void oven_example() { int const a[] = { 0, 1, 6, 2, 6, 6, 5, 2, 7, 5 }; BOOST_CHECK(distance(a | sorted | uniqued) == 6); }
コードは簡潔で,sorted や uniqued という言葉から,容易にその意味を察することができます.この威力を感じて下さい.
C++11 での Range
C++11 で Range Algorithm が標準ライブラリに取り入れられることはありませんでしたが,言語のコア機能の一部で Range をサポートしています.Range-based for です.これは他の言語で言うところの foreach に当たります.使ってみましょう.
// 基本 int a[] = { 1, 2, 3, 4, 5 }; for (auto i : a) std::cout << i << '\n'; // 要素を変更する for (auto &i : a) i *= 2; // break,continue も可 std::vector<std::string> const v = { "foo", "bar", "baz", "qux", "quux" }; for (auto const &s : v) { if (s[0] == 'q') break; std::cout << s << '\n'; } // { e1, ..., eN } の形を受け取ることもできる for (auto i : { 1, 2, 3, 4, 5 }) std::cout << i << '\n';
boost::find_if + ラムダ式でも近いことはできますが,C++11 のラムダ式は少々貧弱ですし,直観的に書ける range-based for の出番は多いと思います.GCC 4.6 以降,Clang 3.0 以降でサポートされているので,積極的に使っていきましょう.え,VC?*2
Egg の楽しみ
これは Boost Advent Calendar 2011 の参加記事 (11 日目)です.
今日はせっかくの機会なので,個人的に関心の高い Egg について,ちょっと書いてみたいと思います.Egg を知らない人も多いと思いますので,ここでは入門・解説を書くのではなく,どのようなライブラリなのかをゆるく紹介するような記事にしたいと思います.コード例もありますが,軽く流して雰囲気を感じてください.
Egg とは
id:mb2sync さんの作成された,関数オブジェクトを作るためのライブラリです.
「関数オブジェクトって operator() をオーバーロードするだけで作れるんじゃないの?」と思われた方,全くもってその通りです.しかし前規格 C++03 では,式の型を求める汎用的な方法 (decltype) がなかったため,operator() の戻り値の型を求めるための仕組みを,operator() とは別に用意しておく必要がありました.その仕組みの作成をお膳立てしてくれるのが,Egg に含まれる egg::function です.本稿の最初の方で説明します.
それ以外にも Egg には,関数オブジェクトを作るための機能がいくつもあります.これらは,新しく関数オブジェクトを作る Function Builders,既存の関数オブジェクトを元に関数オブジェクトを作る Function Adaptors,有名な関数テンプレートを関数オブジェクトにした Function Objects に分けられます.本稿の真ん中くらいで紹介します.
本稿の最後の方では,C++11 での Egg について,私の考えるところを書きます.
2 つの Egg
Egg には,P-Stade C++ Libraries に含まれるものと,Boost に Boost.Egg として提案されたものとがありますが,基本的な部分は同じです.以降のコード例では Boost.Egg を使用しますが,BOOST_EGG_* となっているマクロを PSTADE_EGG_* に読み替えれば P-Stade でもそのまま使えます.インストールする方法は各リンク先を参照してください.
egg::function
では始めましょう.一から関数オブジェクトを作るときの強力なヘルパー,egg::function から紹介します.
先程戻り値の型がどうこう書きましたが,改めて説明します.C++03 では,(boost::result_of によって) 戻り値の型を求めることができる関数オブジェクトを書くのは実に面倒でした.それを示すために,引数への参照をそのまま返す関数オブジェクト identity を C++03 で書いてみます.
// これに相当するものを書く /* template <class T> inline T &identity(T &t) // lvalue { return t; } template <class T> inline T const &identity(T const &t) // rvalue or const lvalue { return t; } */ struct T_identity { template <class FunCall> struct result; template <class Fun, class A1> struct result<Fun(A1 &)> { typedef A1 &type; }; template <class Fun, class A1> struct result<Fun(A1)> { typedef A1 const &type; }; template <class A1> A1 &operator()(A1 &a1) const { return a1; } template <class A1> A1 const &operator()(A1 const &a1) const { return a1; } }; T_identity const identity = {}; // boost::result_of で戻り値の型を求められる BOOST_MPL_ASSERT((boost::is_same< boost::result_of<T_identity(int)>::type, int const & >));
なんか面倒な感じが分かってもらえれば十分です.もうちょっと踏み込むと,クラステンプレート result を宣言して特殊化を 2 つ定義しているところ (ここで戻り値の型を定義します),operator() を 2 つ定義しているところ,そしてその operator() で改めて戻り値の型を書いているところがいかにも冗長です.
egg::function はこの辺をうまく解決します.
struct little_identity { template <class Me, class A1> struct apply { typedef A1 &type; }; template <class Re, class A1> Re call(A1 &a1) const { return a1; } }; typedef function<little_identity> T_identity; T_identity const identity = {{}};
一気に簡単になりました.クラステンプレート apply の定義を 1 つ,メンバ関数テンプレート call の定義を 1 つ書くだけで済んでおり,call の戻り値の型もテンプレート引数で Re とだけ書いておけば十分です.
これはかなり単純な例ですが,もっと複雑な関数オブジェクトを書こうとすると,egg::function によって省かれる労力はかなり大きくなります.C++03 で関数オブジェクトを書くときに少しでも面倒だと感じたら,Egg の利用を考慮すると良いでしょう.詳しい使い方はリファレンスを参照してください.
ちょっと寄り道:boost::forward_adapter
Egg がない場合,boost::forward_adapter が役に立つかもしれません.上の例だと,
struct imperfect_identity { template <class FunCall> struct result; template <class Fun, class A1> struct result<Fun(A1 &)> { typedef A1 &type; }; template <class A1> A1 &operator()(A1 &a1) const { return a1; } }; typedef boost::forward_adapter<imperfect_identity> T_identity; T_identity const identity;
となります.operator() の戻り値の型については egg::function ほど簡単にはなりませんが,Boost に入っているので気軽に使えると思います.
Function Builders
ピッチを上げて次々に Egg の機能を紹介していきます.面白いと思うものがあれば遊んでみましょう.
Function Builders は,新しい関数オブジェクトを作る機能です.先ほどの egg::function もここに含まれます.
ここでは generator を紹介しましょう.generator を使って作られた関数オブジェクトは,引数の型と値を元に新しいオブジェクトを生み出します.次の例は,make_pair を作るものです.
typedef generator< std::pair< deduce<boost::mpl::_1, as_value>, deduce<boost::mpl::_2, as_value> > >::type T_make_pair; T_make_pair const make_pair = BOOST_EGG_GENERATOR(); void egg_example() { BOOST_CHECK(make_pair(42, 3.14) == std::make_pair(42, 3.14)); }
Function Adaptors
既存の関数オブジェクトから新しい関数オブジェクトを生み出す機能です.関数合成,カリー化,不動点コンビネータ (さらにメモ化) など,関数型言語が好きな人なら気に入りそうな機能が盛りだくさんです.カリー化の例を示しましょう.
struct base_my_plus { typedef int result_type; int operator()(int x, int y, int z) const { return x + y + z; } }; result_of_curry3<base_my_plus>::type const curried_my_plus = BOOST_EGG_CURRY3({}); void egg_example() { BOOST_CHECK(curried_my_plus(1)(2)(3) == 6); }
Oven の Range Adaptors の実装に使われている pipable もここにあります.拡張メソッドライクなものを作る場合に役立つでしょう.例は,Function Builder の implicit との合わせ技で T t = "文字列" | parse; を可能にするものです.
template <class To> struct X_lexical_cast { typedef To result_type; template <class From> To operator()(From const &from) const { return boost::lexical_cast<To>(from); } }; typedef result_of_pipable< implicit<X_lexical_cast<boost::mpl::_1> >::type >::type T_parse; T_parse const parse = BOOST_EGG_PIPABLE(BOOST_EGG_IMPLICIT()); void egg_example() { int const i = "42" | parse; BOOST_CHECK(i == 42); }
また寄り道:static initialization
関数オブジェクトはヘッダに書いておくと便利です.しかし,ヘッダで名前空間スコープにオブジェクトを定義した場合,一般には ODR と初期化の順序が問題になります.
ODR については,関数オブジェクトを const にしておけばほとんど問題は生じないと考えられます.まじめに考え出すと泥沼なので (参照 1,参照 2),そういうことにしておきましょう.
さて,初期化の順序ですが,ローカルでない静的オブジェクトの初期化の順番は一般には定まりません (Effective C++ 4 項など).しかしある種の初期化は,順序を気にせずに (その他の種類の初期化より前に) 行われることが保証されており,static initialization と呼ばれます (3.6.2).具体的には,最初のゼロ初期化と定数式での初期化がこれに当たります.例えば,次のオブジェクト a の初期化は static initialization です.
struct A { int i; }; A const a = { 42 };
Static initialization は,初期化順の問題を解決すると同時に,実行時のオーバーヘッドを減らす効果も期待されます.そこで Egg は,static initialization になる初期化子をマクロで提供しています.これまでの例に登場した BOOST_EGG_GENERATOR() などの奇妙なマクロがそうで,BOOST_EGG_GENERATOR() は {} に展開されます.
C++11 では定数式の範囲が広がったため,これらのマクロは不要になると考えられます.
Function Objects
最後に紹介する機能は,有名な関数テンプレートを関数オブジェクトにした Function Objects です.ここでは boost::get を元にした get を紹介しましょう.これは boost::tuple だけでなく,あらゆる Fusion 列から指定した位置の要素を抜き出します.身近なところでは,std::pair の最初の要素を取る関数オブジェクトを boost::mem_fn(&std::pair<first_type, second_type>::first) から X_get_c<0>() に変えるなどできます.
void egg_example() { using phx::arg_names::arg1; using phx::arg_names::arg2; std::vector<std::pair<int, std::string> > adc = boost::assign::pair_list_of (3, "hotwatermorning")(1, "cpp_akira")(2, "kikairoya")(4, "Flast_RO"); // 最初の要素でソートする boost::sort(adc, phx::bind(X_get_c<0>(), arg1) < phx::bind(X_get_c<0>(), arg2)); }
C++11 での Egg
C++11 では,関数オブジェクトを書くのがとても楽になりました.ほとんどの場合,operator() を定義しておけばそれで十分で,しかも Perfect Forwarding のためにずらずらとオーバーロードを並べる必要もありません.最初の identity を書き直してみましょう.
struct T_identity { template <class T> constexpr T &&operator()(T &&t) noexcept { return std::forward<T>(t); } }; constexpr T_identity identity = {};
とても簡単になりました.C++11 では,egg::function の必要性は薄れていくと思われます.しかし Egg には本稿で紹介したような興味深い機能が他にいくつもあり,それらは C++11 でも有用です.これらは内部で boost::result_of を使っているので,BOOST_RESULT_OF_USE_DECLTYPE マクロを定義しておくと幸せになれるでしょう.
さて,C++11 流の Perfect Forwarding を求める人や,「全部 constexpr でやりたいんだ!」というアレな先進的な人には多少の不満が残るかもしれません.そこで実験がてら,C++11 で Egg を書き直してみました.
https://github.com/iorate/Egg
GCC 4.7.0 の -std=c++11 を前提に書かれています.また,C++03 の制限に由来すると思われる機能はばっさり切り捨てています.使用例を示しましょう.
struct base_my_plus { template <class T, class U> constexpr auto operator()(T &&t, U &&u) -> decltype(std::forward<T>(t) + std::forward<U>(u)) { return std::forward<T>(t) + std::forward<U>(u); } }; constexpr auto my_plus = pipable(base_my_plus()); static_assert((1 | my_plus(2)) == 3, "");
result_of_* や奇妙な初期化子マクロが消え,またコンパイル時実行をサポートしている点に注目してください.
実は寄り道の方が本編:Forwarding Strategies
C++03 で Perfect Forwarding を実装するのはものすごく大変でした (参照 3.「簡単でしょ?」と思った人は魔クロに毒されています).書くのが面倒なだけならともかく,サポートする引数の数を増やすとコンパイル時間が爆発する危険もありました.
そこで Egg では,perfect ではないが軽量な forwarding 戦略をいくつか提供しています.たとえば by_cref は,引数を const 参照で転送します.
struct little_size { template <class Me, class Range> struct apply : boost::range_difference<Range> {}; template <class Re, class Range> Re call(Range const &rng) const { return boost::size(rng); } }; typedef function<little_size, by_cref> T_size; T_size const size = {{}};
C++11 ではこれらの戦略の必要性はあんまりないと考えたため,上の Egg もどきでは Perfect Forwarding のみをサポートしています.
おわりに
ずいぶん長くなりましたが,C++03 で一から関数オブジェクトを書くときは egg::function が便利で,それ以外にも面白い関数オブジェクトを作る機能がいくつもあるよ,という話でした.
またインターフェース,実装ともに大変よく考えられていて勉強になるので,ライブラリ寄りな人は読んでみると面白いと思います.
明日は id:gintenlabo さんによる Boost.Chrono の紹介です.
何が lvalue で何が rvalue なのか
こういう記事も必要かもしれないと思いました.内容はタイトルそのままです.
はじめに
はじめに,と書きましたがこの項は飛ばしても問題ないです.
まず "lvalue" とか "rvalue" とかいうものが何についての概念なのかということですが,これらは C++ の "式" の持つ属性 "value category" の値です."式" には他に "型" という属性があります.
int i = 0; i; // 式 i の型は int,value category は lvalue 0; // 式 0 の型は int,value category は rvalue
型の決め方は難しくないと思うので (たぶん),この記事では value category の決め方を書きます.以降,"i は int の lvalue である" といった言い方をします.
Lvalue,rvalue のイメージ
いきなりですが引用です.
誤解を恐れずにいえば、lvalueとは、明示的に実体のある、名前付きのオブジェクトであり、rvalueとは、一時的に生成される無名のオブジェクトである。
http://cpplover.blogspot.com/2009/11/rvalue-reference_23.html
int i = 0; i; // 式 i は lvalue 0; // 式 0 は rvalue
この説明が一番分かりやすいです.まずこのイメージを何となくつかみます.
Lvalue になるもの
では lvalue になる主なものを挙げていきます.
変数
int i = 0; i; // i は int の lvalue int &ri = i; ri; // ri は int の lvalue int &&rri = 0; rri; // rri は int の lvalue
式 ri の型を int & でなく int と書いていますが,一般に式の型を考えるときに参照は考えませんので*1,そういう書き方になっています.
注意すべきなのは 3 例目です.変数 rri の型が int && であることに惑わされてはいけません.変数名を書いた式は,lvalue なのです.
Lvalue のメンバ変数
struct A { int i; }; A a = {0}; a; // a は A の lvalue a.i; // a.i は int の lvalue
Lvalue はメンバ変数に伝播します.
ポインタを * したもの
int *pi = &i; *pi; // int の lvalue (i と同じオブジェクトを指している)
アドレスを取れているなら,その指している先は "明示的な実体" と考えられます.
ではちょっと応用です.先程の,lvalue のメンバ変数は lvalue,という話と組み合わせてみましょう.
A *pa = &a;
pa->i; // int の lvalue
pa->i は (*pa).i と等価ですが,(*pa) が lvalue なので,pa->i == (*pa).i も lvalue になります.
Lvalue reference を返す関数の呼び出しやそれに準ずるもの
int &rf(); rf(); // int の lvalue static_cast<A &>(a); // A の lvalue A b; a = b; // A の lvalue
Lvalue reference というのは文字通り lvalue への参照なので,lvalue reference を返してきたら lvalue と思っていいわけです.
Prvalue になるもの (rvalue その 1)
新しい用語 prvalue を出してしまいましたが,rvalue は xvalue と prvalue に分けられ,その一つです.これは昔ながらの rvalue で,"一時的に生成される無名のオブジェクト" というイメージそのままです.
基本
int(); // int の prvalue A{0}; // A の prvalue
Prvalue のメンバ変数
A().i; // int の prvalue
Rvalue も,基本的にはメンバ変数に伝播します.
参照以外を返す関数の呼び出しとそれに準ずるもの
int f(); f(); // int の prvalue static_cast<double>(i); // double の prvalue 2 + 3; // int の prvalue new int; // int * の prvalue
多くの関数呼び出しや組み込み演算子呼び出しがここに属します.
Xvalue (rvalue その 2)
前述の通り rvalue の一種です.
Xvalue のメンバ変数
これまでと同様です.
Rvalue reference を返す関数の呼び出しとそれに準ずるもの
int &&rrf(); rrf(); // int の xvalue static_cast<A &&>(a); // A の xvalue
これも rvalue への参照を返しているから rvalue だろう,ということですね.
ここに std::move の呼び出しが含まれることは重要です.
namespace std { template <class T> typename remove_reference<T>::type &&move(T &&t) noexcept { return static_cast<typename remove_reference<T>::type &&>(t); } } std::move(a); // A の xvalue
std::move の格好は仰々しいですが,戻り値の型が rvalue reference になっている点に注目しましょう.std::move はオブジェクトを rvalue に変える関数である,ということが理解できると思います.
おわりに
とりあえず思いつくところを挙げてみました.他に重要なものがあれば教えてください.
*1:5/5
構造体を tuple-like としてアダプトするマクロを書いた
C++11 の規格には,tuple-like という言葉が登場します.これは所謂コンセプトで,tuple-like コンセプトを満たす型 T は,std::tuple_size<T>,std::tuple_element<I, T>,get<I>(t) を持ちます.標準で定義されたものでは,std::pair<T1, T2>,std::tuple<Types...>,std::array<T, N> がこれを満たします.
typedef std::pair<int, double> pair_t; // std::tuple_size static_assert(std::tuple_size<pair_t>::value == 2, ""); // std::tuple_element static_assert(std::is_same<std::tuple_element<0, pair_t>::type, int>::value, ""); // get pair_t p = { 42, 3.14 }; assert((&std::get<0>(p) == &p.first));
さて,ユーザー定義型をこれにアダプトするマクロを書いてみました.
https://github.com/iorate/std-tuple-adapted
使い方は BOOST_FUSION_ADAPT_STRUCT などと似ています.
namespace NS { struct A { int i; double d; }; } IORATE_STD_TUPLE_ADAPT_STRUCT((NS), (A), (i)(d)) void example1() { // std::tuple_size static_assert(std::tuple_size<NS::A>::value == 2, ""); // std::tuple_element static_assert(std::is_same<std::tuple_element<0, NS::A>::type, int>::value, ""); // get NS::A a = { 42, 3.14 }; using std::get; assert((&get<0>(a) == &a.i)); }
注意すべき点として,get<I> は ADL で呼び出すわけですが,その前に get という名前の関数テンプレートを見えるようにしておかなければなりません*1.もっとも,予め using std::get; としておけばよいので,swap と使い方は同じになります.boost::swap に相当するものがあると便利でしょう.上の所に一緒においてあります.
void example2() { std::pair<int, double> p = { 42, 3.14 }; assert((&iorate::get<0>(p) == &p.first); NS::A a = { 42, 3.14 }; assert((&iorate::get<0>(a) == &a.i)); }
また,クラステンプレート用のマクロもあります.
namespace NS { template <class T> struct X { T t; }; } IORATE_STD_TUPLE_ADAPT_TPL_STRUCT((NS), (class T), (X<T>), (t))
最後に,このアダプトが何の役に立つかということですが*2,tuple-like コンセプトを利用したライブラリを使う際に役に立ちます.そのままですね.現在あるライブラリでは,std::tuple_cat などがあります.…それくらいしかありませんが.
random_shuffled range アダプタ
久しぶりに range アダプタでも.
考え方は oven::sorted と同じで,range からイテレータのリストを作り,そのリストをシャッフルします.結果として forward range にも対応できます.
random_shuffled.hpp (要 P-Stade)
#ifndef IORATE_RANDOM_SHUFFLED_HPP #define IORATE_RANDOM_SHUFFLED_HPP #include <boost/concept/assert.hpp> #include <boost/range/algorithm/random_shuffle.hpp> #include <boost/range/concepts.hpp> #include <boost/utility/result_of.hpp> #include <pstade/egg/function.hpp> #include <pstade/egg/pipable.hpp> #include <pstade/oven/indirected.hpp> #include <pstade/oven/outplaced.hpp> namespace iorate { namespace random_shuffled_detail { using namespace pstade::oven; struct little { template <class Me, class Range, class RandomNumberGenerator = void> struct apply : boost::result_of< T_make_indirected( typename boost::result_of<T_make_outplaced(Range &)>::type & ) > {}; template <class Re, class Range> Re call(Range &rng) const { BOOST_CONCEPT_ASSERT((boost::ForwardRangeConcept<Range>)); typename boost::result_of<T_make_outplaced(Range &)>::type its = rng | outplaced; boost::random_shuffle(its); return its | indirected; } template <class Re, class Range, class RandomNumberGenerator> Re call(Range &rng, RandomNumberGenerator &rand) const { BOOST_CONCEPT_ASSERT((boost::ForwardRangeConcept<Range>)); typename boost::result_of<T_make_outplaced(Range &)>::type its = rng | outplaced; boost::random_shuffle(its, rand); return its | indirected; } }; } // namespace random_shuffled_detail typedef pstade::egg::function<random_shuffled_detail::little> T_make_random_shuffled; T_make_random_shuffled const make_random_shuffled = {{}}; pstade::egg::result_of_pipable<T_make_random_shuffled>::type const random_shuffled = PSTADE_EGG_PIPABLE({{}}); } // namespace iorate #endif
random_shuffle_test.cpp
#include <iostream> #include <boost/range/irange.hpp> #include <pstade/oven/io.hpp> #include "./random_shuffled.hpp" int main() { std::cout << (boost::irange(0, 10) | iorate::random_shuffled) << '\n'; }
$ g++ -I $BOOST_ROOT -I $PSTADE_ROOT random_shuffle_test.cpp $ ./a {8,1,9,2,0,5,7,3,4,6}
constexpr の再帰上限数
512 回以上であることが推奨されています.GCC 4.6.1 ではデフォルトで 512 回です.
repeated.cpp
// apply f n times to x template <class F, class T> constexpr T repeated(F f, unsigned n, T x) { return n == 0 ? x : repeated(f, n - 1, f(x)); } constexpr int inc(int x) { return x + 1; } static_assert(repeated(inc, 1000, 0) == 1000, "");
$ g++ -c -std=c++0x repeated.cpp repeated.cpp:5:48: in constexpr expansion of 'repeated [with F = int (*)(int), T = int](f, (n + -1u), f(x))' ... repeated.cpp:5:48: in constexpr expansion of 'f(x)' repeated.cpp:9:1 error: constexpr evaluation depth exceeds maximum of 512 (use -fconstexpr-depth= to increase the maximum)
上限を増やすには,-fconstexpr-depth= オプションを使います.
$ g++ -c -std=c++0x repeated.cpp -fconstexpr-depth=1001
あまり再帰が深すぎると,コンパイラがクラッシュしてしまうことがあります.再帰が浅くなるよう書き換えましょう.
// apply f n times to x template <class F, class T> constexpr T repeated(F f, unsigned n, T x) { return n == 0 ? x : n == 1 ? f(x) : repeated(f, n / 2, repeated(f, n - n / 2, x)); } constexpr int inc(int x) { return x + 1; } static_assert(repeated(inc, 1000000, 0) == 1000000, "");
$ g++ -c -std=c++0x repeated.cpp
コンテナの要素を別のコンテナに移動する
コンテナごとムーブ
std::vector<T> v1; // ... std::vector<T> const v2(std::move(v1));
move_iterator を使ってムーブ
std::vector<T> v; // ... std::list<T> const l( std::make_move_iterator(v.begin()), std::make_move_iterator(v.end()));
move アルゴリズムを使ってムーブ
std::vector<T> v1;
std::vector<T> v2;
// ...
v2.reserve(v2.size() + v1.size());
std::move(v1.begin(), v1.end(), std::back_inserter(v2));
splice メンバ関数を使う (std::list)
std::list<T> l1;
std::list<T> l2;
// ...
l2.splice(l2.end(), l1);
などなど.