Universal Reference is 何
universal reference という文字が見えたけど聞いたことなかったのでまとめました。
知識としては皆さんご存知だと思いますので復習程度に見ていただければ幸いです。
知らなかった人はこの記事で入門しましょう!
Scott Meyers氏による universal reference の 定義
If a variable or parameter is declared to have type T&& for some deduced type T, that variable or parameter is a universal reference.
あれ…この記事これでおしまいでいいんじゃ…?
&& の持つ意味
もう少しお付き合いください。
void f( std::vector<int>&& v ) {} // 1 template <typename T> // 2 void f( T&& t ) {} // some_class というクラスがあったとする some_class sc1; some_class&& sc2 = sc1; // 3 auto&& sc3 = sc2; // 4
結論からいうと2、4の T&&, auto&& が universal reference にあたるわけですが、ちょっくら説明いたします。
"&&" を見たら無条件で「あっ!rvalue reference だ!」となる方もいらっしゃることと思いますが、早計です。落ち着きましょう。
ここで問題になってくるのが、Tという型が deduced type である場合、単純に "T&&" と書いただけではこれは rvalue reference にも lvalue reference にもなりうる、ということです。
簡単にいえば、
- lvalue によって初期化が行われれば universal reference は lvalue reference になる
- rvalue によって初期化が行われれば universal reference は rvalue reference になる
ということです。(lvalue やら rvalue やらわからない人は僕の前の記事や、C++の言語規格を参照してください)
というわけで、元のコードをもっかい見てみましょう。
int x = 0; f( x ); // 1: x は lvalue なので、T&& は int& になる f( 0 ); // 2: 0 は rvalue なので、T&& は int&& になる // 4: sc2 は lvalue なので、some_class& sc3 = sc2; と同じ意味になる auto&& sc3 = sc2;
もうお分かりですね?
気をつけるべき universal reference
このコードは universal reference でしょうか?
template <typename T> void f( std::vector<T>&& v ) {}
答えは No. です。なぜなら "&&" が T にかかってないからです。
では、これはどうでしょう。
template <typename T> void f( T const&& t ) {}
答えは No. です。const をつけたら universal reference の要件を満たさないようです。
なので T const&& は rvalue reference になります。
じゃあこれは…? vector<T>::push_back(T&& t) の T&& は universal reference でしょうか?
template <typename T/*, class Allocator = allocator<T>*/> class vector { public: void push_back( T&& t ); };
答えは No. だそうです。なぜなら、vectorを使うときは vector<int> のように T の型がわかりますので、type deduction が発生しないからです。つまり、T&& は rvalue reference といえます。
(と言っていますが、vector<int&> にしたら T&& は lvalue reference となります。だから universal reference といってもいいんじゃないかと個人的には思っています。type deduction が発生しないので定義には当てはまらないですけどね。おそらくですが、
vector<int> v; // vector はさっき書いた vector int i = 0; v.push_back( i ); // error! i は lvalue reference
とエラーになって、T&& と書いているからといって lvalue も rvalue も渡せるわけではないということが言いたいのだと思います。)
最後にこいつはどうでしょう?
template <typename Args...> void f( Args&&... args );
答えは Yes. です。No 続きだったから No だと思った方もおられるでしょうが、これは普通に Yes でいいと思います。
universal reference の注意点とか書きつつ universal reference だったの1つだけじゃねぇかとかいうツッコミは勘弁してください。
最後に
内容的には全く新しくないわけですが、復習にはなったかと思います。
Perfect forwarding(std::forward) とか有名ですが、それにつながる知識ですので知らなかった方は知っておいて損は無いと思います。
Boost.勉強会 #12 大阪に行って来ました
はじめに
寝屋川市の大阪電気通信大学でBoost.勉強会 #12 が開催されました。
僕は発表者として行きましたが、今回もC++erがたくさん集っていたようです。
ちなみに初参加だったので、少し緊張しました。
発表
僕は「Boost.MPLをつかってみよう」という題で発表しました。
スライドにちょっと不備があったので、修正したのをこちらにあげています。
緊張して言いたかったことが多分言えてないのと、日本語がところどころおかしくなってたような気がします。内容もあんまりおもしろくなかったと自分で思ってるので、反省。あと資料はちゃんと前日までに完成させて練習しないとダメですね。申し訳ありませんでした。
発表の内容に関しましては、Boost.MPLは使う機会がないというか、あんまり恩恵を受けてないように感じる方もいらっしゃることと思います。が、普段使ってるBoostのライブラリ等、見えない所で活躍している縁の下の力持ちです。ライブラリを実装している方は、ちょっとだけ使っていることも有るでしょう。大事にしてあげてください。
Twitterで@SubaruGさんが「個人的にはテンプレートメタプログラミングは遅延評価が必要な場合以外は C++11 では使うべきではないと思う. decltype 使おう」とおっしゃられていましたが、自分にはよくわかりませんでした…
最後の補足でADLが~関数ルックアップが~という話がありましたが、ややこしい話なのでもしかしたらまたまとめるかもしれません。
あと、@andochinさんが指摘してくださった以下のコード(Numの部分がtypename又はclassの間違いでは?)ですが
template <Num N>
constexpr N min = std::numeric_limits<N>::min();
ここでのNumはconceptのconstraintsだと思ってください。Conceptの内容が混じってたのでつい書いちゃいました。指摘ありがとうございます。制約しないならtypenameかclassでOKです。
とりあえずconceptはよ
懇親会
闇の軍団ばっかりですごかったです。というか、何気なく喋っていた人が普段Twitterで見かけるすごい人だったりして面白かったです。ご飯よりも話をしてお腹いっぱいになりました。
特に@bolero_MURAKAMIさんとはかなりお話しまして、プログラミングの話だけじゃなくいろんなことを教えてくださりました。ありがとうございました。
C++ なんとかvalues
C++には lvalue やら rvalue やらたくさんありますが、混乱している人もいるかもしれないのでめちゃくちゃ簡単にまとめておきます。
多分間違ってるところがあると思うので、その時は教えて下さい。よろしくお願いします。
lvalue
簡単にいえば左辺にして代入できるもののことです。*1
named object (名前があるオブジェクト)と言ったほうがいいかもしれませんね。
( int& foo() { return i; } foo() = 1; とか書けますよね。)
template <typename T> void f(T& t) { std::cout << "T&" << std::endl; } // lvalue reference template <typename T> void f(T&& t) { std::cout << "T&&" << std::endl; } // rvalue reference int i=0; int& foo() { return &i; } int main() { int xv = 0; // 0 is rvalue(prvalue) f(xv); // T& xv is lvalue f(foo()); // T& foo() is lvalue }
xvalue
C++ の規格より引用します。(§5 の最初あたりですね)
以下の expression はすべて xvalue となります。
- 明示的か非明示的かどうかにかかわらず、戻り値にオブジェクトへの rvalue reference をもつ関数呼び出しの結果
- rvalue reference へのキャスト
- オブジェクトの reference でも static でもないデータメンバへのアクセス
- 1つ目のオペランドが xvalue で、2つ目のオペランドがデータメンバへのオペランドである .* pointer-to-member expression ( ->* だと一つ目のオペランドが xvalue でないのでダメ? )
※ pointer-to-member expression とは
class C { public: constexpr C() : m_(0) {} void disp() { std::cout << m_ << std::endl; } int m_; }; int main() { C c; C* pc = new C(); int C::* pm = &C::m_; c.*pm = 10; pc->*pm = 20; c.disp(); // 10 pc->disp(); // 20 delete pc; }
c.*pm と pc->*pm が pointer-to-member expression です。const object の mutable メンバには使えないとか色々ありますが、詳しくは手元の規格を参照してください。(§5.5 pointer-to-member expression)
一般的に,この規則によって named rvalue references *2 は lvalues として扱われ、unnamed rvalue references to objects*3は xvalues として扱われます。また、関数への rvalue references は名前があるかないかにかかわらず lvalues として扱われます。
struct A { int m; }; A&& operator+(A, A); A&& f(); A a; A&& ar = static_cast<A&&>(a);
f(), f().m, static_cast(a), a + a という expression はすべて xvalue で、ar は lvalue となります。
prvalue(pure rvalue)
xvalue でない rvalue のことです。例えば、戻り値に参照型を持たない関数の呼出の結果は prvalue です。他にも、0 や true 等の文字列以外のリテラルも prvalue です。
prvalue のメンバ変数も prvalue だったり。
【追記 2013/05/16】ラムダ式も prvalue ですね。
template <typename T> void f(T& t) { std::cout << "T&" << std::endl; } // lvalue reference template <typename T> void f(T&& t) { std::cout << "T&&" << std::endl; } // rvalue reference int i=0; int bar() { return 1; } struct hoge { int m; }; int main() { f(1); // T&&: 1 is prvalue f(bar()); // T&&: bar() is prvalue f(false); // T&&: false is prvalue hoge().m; // prvalue }
glvalue
lvalue または xvalue の事です。(すみませんこれ以上の説明が思い浮かびませんでした。)
rvalue
大雑把に言いますと、一時オブジェクトのことです。xvalue と prvalue をまとめたものとして考えられます。
unnamed object(名前がないオブジェクト)とも言えます。
C++11の rvalue reference とは一時オブジェクトを保持するためのものですが、
std::string str1 = "bar"; std::string&& str2 = "foo"; std::string&& str3 = str1; // Error! str1 is lvalue std::string&& str4 = str2; // Error! str2 is lvalue
3行目は明らかにエラーですね。
4行目で str1 は名前を持ってしまっている(一時オブジェクトでない)ので rvalue ではなく lvalue となり、エラーとなります。
最後に
もっと詳しい解説は、ググるかなにかで調べてもらえれば同志の方がたくさん書かれていらっしゃいますので、そちらに丸投げさせて頂きます。あくまで簡単なまとめということでお願いします。
C++ Template Metaprogramming の Exercise 4-2
Variadic Templates Verで書いた。
struct error {}; template< bool B, typename ... Args > struct logical_and_; template< typename head, typename ... tail > struct logical_and_<false, head, tail...> { typedef mpl::false_ type; }; template< typename head, typename ... tail > struct logical_and_<true, head, tail...> { typedef typename logical_and_<head::type::value, tail...>::type type; }; template< typename T > struct logical_and_<true, T> { typedef typename T::type type; }; template< typename head, typename ... tail > struct logical_and { static const bool value = head::type::value; typedef typename logical_and_<value, tail...>::type type; }; template< bool B, typename ... Args > struct logical_or_; template< typename head, typename ... tail > struct logical_or_<false, head, tail...> { typedef typename logical_or_<head::type::value, tail...>::type type; }; template< typename T > struct logical_or_<false, T> { typedef typename T::type type; }; template< typename head, typename ... tail > struct logical_or_<true, head, tail...> { typedef mpl::true_ type; }; template< typename head, typename ... tail > struct logical_or { static const bool value = head::type::value; typedef typename logical_or_<value, tail...>::type type; }; int main() { BOOST_STATIC_ASSERT(( logical_or<mpl::true_, error>::type::value )); BOOST_STATIC_ASSERT(( !logical_and<mpl::false_, error>::type::value )); BOOST_STATIC_ASSERT(( logical_or<mpl::false_, mpl::false_, mpl::true_, error>::type::value )); BOOST_STATIC_ASSERT(( !logical_and<mpl::true_, mpl::true_, mpl::false_, error>::type::value )); }
まだ慣れてない。
mpl::equalに渡すところをstd::is_sameにしてしまった
ちょっとハマったのでメモ。
ある日、以下の様なコードを書いてました。
typedef mpl::vector_c<int, 1, 2, 3> foo; typedef mpl::transform<foo, mpl::plus<_1, mpl::int_<1>>>::type bar; typedef mpl::vector_c<int, 2, 3, 4> expected; int main() { BOOST_STATIC_ASSERT(( std::is_same<bar, expected>::value )); }
てっきり mpl::transform で mpl::vector_c が返ってくると期待していた私は、BOOST_STATIC_ASSERTで失敗しているのに気がついてかなりびっくりしました。
ここでの mpl::transform によって返ってくる型は
struct mpl::vector3< struct mpl::integral_c<int, 2>, struct mpl::integral_c<int, 3>, struct mpl::integral_c<int, 4> >
ぜんぜん違うじゃねーか
なので、多分 std::is_same と相性そんなに良くないのかもしれません。
素直に mpl::equal 使いましょう。
BOOST_STATIC_ASSERT(( mpl::equal<bar, expected>::value ));
xrandr で 1920x1080 が出てくれない時の対処法
http://samuelmartin.wordpress.com/2012/05/29/enabling-resolutions-in-ubuntu-12-04-lubuntu-12-04/
ここに書いてあるとおり、まずターミナルで
gtf 1920 1080 60
と入力すると、
# 1920×1080 @ 60.00 Hz (GTF) hsync: 67.08 kHz; pclk: 172.80 MHz Modeline "1920x1080_60.00" 172.80 1920 2040 2248 2576 1080 1081 1084 1118 -HSync +Vsync
のように出力されると思います。
その後、ターミナルで
xrandr
と入力し、接続されているモニタを確認します。
Screen 0: minimum 1 x 1, current 1920 x 1080, maximum 8192 x 8192 Virtual1 connected 1920×1080+0+0 0mm x 0mm
上記のように出力されているなら、次に進みましょう。
その後、 /usr/share/X11/xorg.conf.d/10-monitor.conf を作成し、以下のように編集します。
Section "Monitor" Identifier "Monitor0" Modeline "1920x1080_60.00" 82.97 1000 1064 1168 1336 1000 1001 1004 1035 -HSync +Vsync EndSection Section "Screen" Identifier "Screen0" Device "Virtual1" Monitor "Monitor0" DefaultDepth 24 SubSection "Display" Depth 24 Modes "1920x1080_60.00" "1024x768" EndSubSection EndSection
Modeline は xrandr で出力された "1920x1080_60.00" 172.80 1920 2040 2248 2576 1080 1081 1084 1118 -HSync +Vsync の部分、Device は Virtual1 の部分、 Modes は "1920x1080_60.00" の部分です。
その他は書いてある通りで構いません。
その後、保存してrebootすれば適用されてるはずです。
Boost.Contract の出力先を変更する
あけましておめでとうございます。今年もよろしくお願いします。
さて、新年一発目の記事はタイトルにあるとおりです。
そのコードが以下になります。
//main.cc #include <contract.hpp> #include <boost/exception/all.hpp> #include <iostream> #include <fstream> class my_exception : public boost::exception, public std::exception {}; CONTRACT_FUNCTION( void (func) ( const int arg ) precondition( arg >= 0 ? true : throw my_exception() ) // 違反なら例外を投げる ) { // ... } void throwing_handler( const contract::from& context ) { // ここでは出力先をファイルにしています const std::string log_file_path( "./contract_log.txt" ); std::ofstream ofs( log_file_path ); try { // 投げられている例外(裏で溜まってる?)をcatchさせる throw; } catch(contract::broken& failure) // contract の assertion で // false になればこれが投げられる { ofs << failure.file_name() << "(" << failure.line_number() << "): contract assertion \"" << failure.assertion_code() << "\" failed " << std::endl; } catch(std::exception& failure) // ユーザーがstd::exceptionを継承して作った例外 // (example. my_exception)とか、 // 標準の例外(example. std::bad_alloc) // が投げられたらこっち { ofs << "contract failed: " << failure.what() << std::endl; } catch(...) // その他 { ofs << "contract failed with unknown error" << std::endl; } // デストラクタからの例外は投げない! // すなわち、std::terminateとはおさらばさ! if( context == contract::FROM_DESTRUCTOR ) { ofs << "exception from destructor!"; } else { // re-throwすることで、先ほどのアクティブな例外を伝播させる throw; } } int main() { // precondition と postcondition でのアサーションを // 捉える関数をthrowing_handlerに設定する // ほかにも、contract::set_class_invariant_broken, // contract::set_block_invariant_broken // 等がある contract::set_precondition_broken( &throwing_handler ); contract::set_postcondition_broken( &throwing_handler ); try { func( -1.0 ); } catch( const std::exception& e ) // throwing_handlerの最後で // re-throwされたのがここでcatchされる { // こっちは標準エラー出力 std::cerr << e.what() << std::endl; } }
ちなみに、contract::brokenはstd::logic_errorを継承していて、std::exceptionで捉えることが可能です。
標準のthrowing_handlerにあたるdefault_broken_handlerという関数は、デストラクタから例外が投げられたら常にstd::terminateを呼び出してます。ユーザーがset_precondition_broken等を呼びだし、上記のようなハンドリングをすることでそれを防ぐこともできます。
余談ですが、僕はこんな感じで書いてます。
void throwing_handler( const contract::from& context ) { if( context == contract::FROM_DESTRUCTOR ) { //... } else { throw; } } int WINAPI _tWinMain( HINSTANCE, HINSTANCE, LPTSTR, int ) { try { contract::set_precondition_broken( &throwing_handler ); contract::set_postcondition_broken( &throwing_handler ); //... } catch( const boost::exception& e ) { //... } catch( const std::exception& e ) { //... } catch( ... ) { //... } return 0; }
throwing_handlerがえらくすっきりしていますが、Main関数のほうで大きくtryで囲ってるので、ちゃんと例外が拾えます。
この書き方だと、contract以外でMainまで漏れてきた例外を全部catchできるので、結構好きなんですが、どうなんでしょうか。