GCC Bug 58046 - template operator= in SFINAE class
コンパイルエラーになって欲しかったのにICEになりやがったのでバグレポ。
SFINAE 使ってたらなぜかバグりました。でも少し変えたら動くんですよねー。
詳細はリンク先を参照ください。
バグレポの書き方とか教えてもらえばよかったかなぁ…
Boost.Log 追記モード・ユーザー定義の severity
Boost.Logはファイルに書き出せるわけですが、追記モードも可能です。
ついでに、自分で定義した severity を書きだす方法もどーぞ。
#include <boost/log/utility/setup/file.hpp> #include <boost/log/utility/setup/common_attributes.hpp> #include <boost/log/trivial.hpp> #include <boost/log/attributes.hpp> #include <boost/log/expressions.hpp> #include <boost/log/support/date_time.hpp> namespace logging = boost::log; namespace attrs = boost::log::attributes; namespace keywords = boost::log::keywords; namespace sinks = boost::log::sinks; namespace expr = boost::log::expressions; enum severity_level { debug, warning, error, critical }; struct severity_tag; std::ostream& operator<< ( std::ostream& strm, severity_level level ) { static const char* strs[] = { "debug", "warning", "error", "critical" }; if ( static_cast< std::size_t >( level ) < sizeof( strs ) / sizeof( *strs ) ) strm << strs[level]; else strm << static_cast< int >( level ); return strm; } int main() { logging::register_simple_formatter_factory< severity_level, char >( "Severity" ); logging::add_file_log( keywords::file_name = "logs/%Y%m%d.log", keywords::time_based_rotation = sinks::file::rotation_at_time_point( 0, 0, 0 ), keywords::format = ( expr::stream << expr::attr< unsigned int >( "LineID" ) << ":" << "[" << expr::format_date_time< boost::posix_time::ptime >( "TimeStamp", "%Y-%m-%d %H:%M:%S" ) << "][" << expr::attr< severity_level, severity_tag >( "Severity" ) << "]: " << expr::smessage ), // 又は、単に // keywords::format = "%LineID%:[%TimeStamp%][%Severity%]:%Message%" keywords::open_mode = (std::ios::out | std::ios::app) // std::ios のがそのまま使える ); logging::sources::severity_logger<severity_level> slg; logging::add_common_attributes(); // LineID を使えるようにする logging::core::get()->add_global_attribute( "TimeStamp", attrs::local_clock() ); BOOST_LOG_SEV( slg, debug ) << "hoge"; BOOST_LOG_SEV( slg, error ) << "bar"; BOOST_LOG_SEV( slg, warning ) << "piyo"; BOOST_LOG_SEV( slg, critical ) << "foo"; }
ログファイルがこちら
1:[2013-08-02 19:15:24][debug]: hoge 2:[2013-08-02 19:15:24][error]: bar 3:[2013-08-02 19:15:24][warning]: piyo 4:[2013-08-02 19:15:24][critical]: foo
severity に関しては他にも方法あるかも。多分これが一番楽だと思います。
Boost.Logのリファレンス適当に読んだだけなので問題があったら言ってください。
C# で素数列挙した -C# 勉強中
C#の勉強中なので、まずは基本中の基本、素数の列挙をやりました。
もっとかっこよく書けるぞ!って人はコメントください。
using System; using System.Collections.Generic; using System.Linq; using System.Text; namespace ConsoleApplication1 { class Program { static void Main( string[] args ) { Enumerable.Range(2, 100) .Where(x => !Enumerable.Range(2, x - 1).Any(y => x % y == 0)) .ToList() .ForEach(Console.WriteLine); } } }
【追記】
CountじゃなくてAnyを使うようにしました。
あと、Sqrt(x)まででいいじゃんって指摘がありましたが、Rangeの第2引数が end でなく count なので、めんどくさくてそのままです。(Range(begin, end) でなく Range(begin, count)なので…
Universal Reference を知るべき複数の理由
復習第2弾。
universal reference って名前は知らなくていいので、C++のこれらの挙動だけは知っててほしいです。
前の記事と合わせてどうぞ。
Universal Reference は以下 URefs と略させて頂きます。
URefs とオーバーロード
class some_class { public: template <typename T> void f( T const& ) { std::cout << "called T const&" << std::endl; } template <typename T> void f( T&& ) { std::cout << "called T&&" << std::endl; } }; int main() { some_class sc; sc.f( 0 ); int x = 0; sc.f( x ); }
このコードの結果は、
called T&& called T const&
ではありません
正しくはこう
called T&& called T&&
もぅ C++ マジ闇。。。リスカしょ。。。
どうしてこうなった
先日(というか同日)の記事を読んだ人はわかると思いますが、type deduction が発生する場合、template <typename T> some_class::f(T&&) の "T&&" は URefs、すなわち lvalue にも rvalue にもマッチする関数となります。
つまり、さっきの some_class::f の正しい解釈は、
- const lvalue で呼び出された場合、 T const& の f が呼ばれる
- lvalue(not const) または rvalue で呼び出された場合、T&& の f が呼ばれる
となります。
これは、
class some_class { public: void f( const another_class& ); // 1 void f( another_class&& ); // 2 }; another_class make_another_class() { ... } some_class sc; another_class ac; sc.f( ac ); // 1 が呼ばれる sc.f( make_another_class() ); // 2 が呼ばれる
のように、URefs で無い場合と混同してしまうことが非常に多いですので、気をつけましょう。そうでないとプロジェクトが謎のバグで炎上します。
(というかこれ知らなかったら絶対間違えるでしょ…
URefs はとりあえずなんでも食う
std::string search_name( int id ) { ... } class person { public: template <typename T> person( T&& t ) : name( std::forward<T>( t ) ) {} person( int id ) : name( search_name( id ) ) {} private: std::string name; }; std::size_t my_id = 100; person p( my_id ); // error: invalid conversion // from ‘unsigned int’ to ‘const char*’
どうしてこうなった
タイトルに有る通り、 URefs は食いしん坊さんなのでどんな型でもとりあえずマッチします。
今回の例では、 my_id の型が unsigned int です。なので、person(int) よりも person(unsigned int&) になれる URefs のほうが呼ばれてしまいます。型気をつければいいんですけど、人間間違えるときもあるので気をつけましょう。
URefs はやっぱりなんでも食う
class some_class { public: template <typename T> some_class( T&& ) { ... } // URefs ctor template <typename T> some_class& operator=( T&& ) { ... } // URefs copy };
このようにクラスを書いたとします。
template が使われたコンストラクタ、コピー代入演算子は暗黙的に生成されるデフォルトコンストラクタとコピー(ryを抑制しませんので、結局のところ
class some_class { public: template <typename T> some_class( T&& ) { ... } // URefs ctor some_class( some_class const& ) = default; // default copy ctor some_class( some_class&& ) = default; // default move ctor template <typename T> some_class& operator=( T&& ) { ... } // URefs copy some_class& operator=( some_class const& ) = default; // default copy copy some_class& operator=( some_class&& ) = default; // default move copy
となります。
これがどんな悲劇を生むのか、まずはそのコードを見ましょう。
some_class sc; const some_class csc; some_class sc1( sc ); // 1 some_class sc2( csc ); // 2 some_class sc3( std::move( sc ) ); // 3 some_class sc4( std::move( csc ) ); // 4
「え?これ template の部分関係なくね?」と思った人がいるかもしれません。
では、1~4は実際どれを呼んでるのでしょうか。
答えはこれです!
- URefs ctor
- default copy ctor
- default move ctor
- URefs ctor
理由は最初の URefs とオーバーロードに書いてますが、気づかないとやばいです。
謎のバグで残業代も出ないのに会社に泊まり込みになるかもしれません。気をつけましょう。
URefs と initializer_list
template parameter の type deduction が std::initializer_list のときうまく働かない(auto はちゃんとうごく)問題。規格(§14.5.6.2)を見る限りなるほど、って感じですがややこしいのです。stackoverflow にあるので、気になる方は読んでみてください。
stackoverflow -Universal references and std::initializer_list-
どう立ち向かうか
「URefs 使うときはオーバーロードはやめよう」
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 となり、エラーとなります。
最後に
もっと詳しい解説は、ググるかなにかで調べてもらえれば同志の方がたくさん書かれていらっしゃいますので、そちらに丸投げさせて頂きます。あくまで簡単なまとめということでお願いします。