2011年12月28日水曜日

【C++11】 Designated Initializer 風な何か Boost.Fusion 対応

はい、という訳で当初目標であった、Designated Initializer 風な何かの Boost.Fusion 対応ができました。実際のコードは https://github.com/yak1ex/cpp_stuff にあります。GCC 4.7 専用です。書いた後で言うのもなんですが、まぁ余程でかい構造体でなければ全部初期化子書いた方が早いだろ、という気はします。あと最初っからやる気があるなら Boost.Parameter 使えばいいんじゃね?、みたいな。配列の場合は意味があるケースはあるかもしれません。特定の添字だけじゃなくて range<5, 12>() = 1 と書くと 5 ~ 11 が 1 で初期化とかだとより意味があるケースが出てくるかも。

テストコード

#include <vector>
#include <iostream>

#include <boost/fusion/include/adapt_struct.hpp>
#include <boost/fusion/include/define_struct.hpp>
#include <boost/array.hpp>
#include <boost/fusion/include/boost_array.hpp>
#include <boost/tuple/tuple.hpp>
#include <boost/fusion/include/boost_tuple.hpp>
#include <boost/fusion/include/adapt_assoc_struct.hpp>

#include <boost/fusion/include/out.hpp>

#include "dinit.hpp"

// Helper function

template<typename T>
void output(const T& v)
{
 for(auto val : v) {
  std::cout << ' ' << val;
 }
 std::cout << std::endl;
}

// Type definition and adapter

// Don't work for BOOST_FUSION_DEFINE_STRUCT becaues conflict with constructor

struct employee
{
 std::string name;
 int age;
 double rate;
};

BOOST_FUSION_ADAPT_STRUCT(
 employee,
 (std::string, name)
 (int, age)
 (double, rate)
)

namespace keys {
 struct name;
 struct age;
 struct rate;
}

struct employee_
{
 std::string name;
 int age;
 double rate;
};


BOOST_FUSION_ADAPT_ASSOC_STRUCT(
 employee_,
 (std::string, name, keys::name)
 (int, age, keys::age)
 (double, rate, keys::rate)
)

// Don't work for plain array because brace-enclosed initializer is required.

int main(void)
{
 using yak::util::dinit::di;
 using yak::util::dinit::idx;
 using yak::util::dinit::idx_;
 using yak::util::dinit::key;
 using yak::util::dinit::key_;

// For sequence with list constructor

 std::vector<int> v = di(idx<10>() = 1, idx<0>() = 2, idx<2>() = 3, idx<3>() = 4);
 output(v);

 std::vector<int> v2 = di(idx_<10>(1), idx_<0>(2), idx_<2>(3), idx_<3>(4));
 output(v2);

// For Fusion sequence with idx

 employee e = di(idx<2>() = 3.3, idx<1>() = 33);
 boost::fusion::out(std::cout, e); std::cout << std::endl;

 boost::array<int, 5> ar2 = di(idx<3>() = 4);
 output(ar2);

 boost::tuple<std::string, int, double> t = di(idx<1>() = 5);
 boost::fusion::out(std::cout, t); std::cout << std::endl;

// For Fusion sequence with tag

 employee_ e2 = di(key<keys::rate>() = 3.3, key<keys::age>() = 33);
 boost::fusion::out(std::cout, e2); std::cout << std::endl;

 employee_ e3 = di(key_<keys::rate>(3.3), key_<keys::age>(33));
 boost::fusion::out(std::cout, e3); std::cout << std::endl;

 return 0;
}

出力

 2 0 3 4 0 0 0 0 0 0 1
 2 0 3 4 0 0 0 0 0 0 1
( 33 3.3)
 0 0 0 4 0
( 5 0)
( 33 3.3)
( 33 3.3)

2011年12月25日日曜日

【C++11】 Designated Initializer 風な何か シーケンス用

元々、【C++11 Advent Calendar 2011】17日目 小ネタ集 後編用にネタコード作ろうと思ってちょこちょことやっていた訳ですが間に合わなさそうだったので、16進浮動小数点定数やってみたら割とさくっとできたのでそっちだけ記事に書いたわけですが。とりあえずそれっぽいものができたので公開してみます。https://github.com/yak1ex/cpp_stuff の、dinit.hpp, index_tuple.hpp, dinit.cpp になります。とりあえず GCC 4.7 -std=c++0x 限定です。自分が書いてるコードが悪いような気がしますが。後どこかで dangling reference ってる気もします。

#include <vector>
#include <iostream>

#include "dinit.hpp"

int main(void)
{
 using yak::util::dinit::di;
 using yak::util::dinit::idx;
 using yak::util::dinit::idx_;

 std::vector<int> v = di(idx<10>() = 1, idx<0>() = 2, idx<2>() = 3, idx<3>() = 4);
 for(auto val : v) {
  std::cout << val << std::endl;
 }
 std::vector<int> v2 = di(idx_<10>(1), idx_<0>(2), idx_<2>(3), idx_<3>(4));
 for(auto val : v2) {
  std::cout << val << std::endl;
 }
 return 0;
}

出力

2
0
3
4
0
0
0
0
0
0
1
2
0
3
4
0
0
0
0
0
0
1

見ての通り、di() の中で idx<添字>() = 値、あるいは idx_<添字>(値) を並べるとそれに沿って初期化されます。それに沿って初期化されたものによってコピーないしムーブ初期化されます、の方が正確かもしれません。ただ -O2 の段階でも main() 関数内に初期値(2,0,3,4,0,...,0,1)の push が展開されていたのでほぼ初期化相当と見ていいんじゃないでしょうか。↑は vector ですが、initializer-list constructor 持ってるやつ(deque, list, forward_list)なら動いているようです、多分。配列や array にも使えないという残念仕様ですが。また、普通同じ添字のものがあったら最後が有効になると思うでしょうが、最初のものが有効になります。これは直そうと思えばすぐ直せそうですが。

コード的には di() でテンプレート変換演算子を持っているクラスの一時オブジェクトを作成、テンプレート変換演算子中で list initialization している形になります。できればせめて di{} にしたかったのですが早々に諦めました。イメージ的には↓のような感じでこれに実際に実現するためのあれやこれやをつけた感じです。

template<typename Tuple, std::size_t ... idxN>
struct Initer
{
 constexpr Initer(const Tuple& t) : args(t) {}
 template<typename U>
 constexpr operator U()
 {
  return { /* ... */ };
 }
 Tuple args;
};

template<typename ... T>
constexpr const auto di(T&& ... t) -> /* Initer<std::tuple<...>, ...> */;

【C++11】 decltype, conditional operator, そして common_type

C++11 に限らず C++03 的にも基本的なところから不勉強で「そういうことだったのかー」と思うことが多いのであまり自信がないのですが、規格の不整合じゃないかと思われる点を見つけたので書いてみます。基本的に語尾に「と思います、多分」がついていると解釈ください。

とりあえず decltype(e) について FDIS(n3290、自分が持っているのは変更履歴付きの n3291 です) で以下の変更が加えられています(7.1.6.2/4)。

The type denoted by decltype(e) is defined as follows:
  • if e is an unparenthesized id-expression or an unparenthesized class member access (5.2.5), decltype(e) is the type of the entity named by e. If there is no such entity, or if e names a set of overloaded functions, the program is ill-formed;
  • otherwise, if e is a function call (5.2.2) or an invocation of an overloaded operator (parentheses around e are ignored), decltype(e) is the return type of the statically chosen function; an xvalue, decltype(e) is T&&, where T is the type of e;
  • otherwise, if e is an lvalue, decltype(e) is T&, where T is the type of e;
  • otherwise, decltype(e) is the type of e.
The operand of the decltype specifier is an unevaluated operand (Clause 5).

decltype((e)) としては関数呼び出し(オーバーロードされた演算子を含む)の場合はその(静的な)返値の型になるという条件付きの記述から、lvalue なら T&、xvalue なら T&&、prvalue なら T、と分かりやすくなったと言えます。さて、この結果、オーバーロードされていない演算子について、xvalue を返すものに対する delctype((e)) は T から T&& へ挙動が変わったことになります。conditional operator (?:) はオーバーロードできないので常にこの場合に入ります。実際のルールはかなりややこしいのですが第2オペランドと第3オペランドが(lvalue, xvalue, prvalue のカテゴリも含めて)同じ型のケースに限ればその同じ型(とカテゴリ)が全体の型(とカテゴリ)になります。従って以下のようになるはずです。

int&  funcL();
int&& funcX();
int   funcPR();

decltype(true ? funcL()  : funcL())  v1 = funcL();  // int&
decltype(true ? funcX()  : funcX())  v2 = funcX();  // int&&
decltype(true ? funcPR() : funcPR()) v3 = funcPR(); // int

さて、規格中で conditional operator と decltype に依存しているのが common_type です。

// n3291 20.2.4
template <class T>
typename add_rvalue_reference<T>::type declval() noexcept;

// n3291 20.9.7.6/3
template <class T, class U>
struct common_type<T, U> {
 typedef decltype(true ? declval<T>() : declval<U>()) type;
};

declval<int>() の返値の型は int&& であり xvalue です。従って common_type<int, int>::type は int && になってしまいます。decltype の挙動変更前は int でした。これがなぜやばいのか。例えば <chrono> にほ以下のような規定があります。

// 20.11.4.3 common_type specializations
template <class Rep1, class Period1, class Rep2, class Period2>
struct common_type<chrono::duration<Rep1, Period1>, chrono::duration<Rep2, Period2>> {
 typedef chrono::duration<typename common_type<Rep1, Rep2>::type, see below> type;
};

// 20.11.5.5, duration arithmetic
template <class Rep1, class Period1, class Rep2, class Period2>
typename common_type<duration<Rep1, Period1>, duration<Rep2, Period2>>::type
constexpr operator+(const duration<Rep1, Period1>& lhs, const duration<Rep2, Period2>& rhs);
// Returns: CD(CD(lhs).count() + CD(rhs).count())

operator+() の返値の型が common_type を使用し、common_type の特殊化でも common_type を使っているため、chrono::duration<int>() + chrono::duration<int>() の返値の型は chrono::duration<int&&> になります。これは規格の規定からも不正です(Rep は arithmetic type ないし arithmetic type をエミュレーションした class でないと駄目です)。また、そもそも自然な利用方法であろう

template<typename T, typename U>
typename std::common_type<T, U>::type operator+(const T& t, const U& u)
{
 return t+u;
}

に対して少なくとも T, U が同一の型の場合に(普通 operator+ は一時オブジェクトを返すため)、返値の型が rvalue reference で一時オブジェクトを束縛してもコンパイルエラーにならず、速攻で dangling reference を生んでしまいます。

自分の解釈だとこうなるのですが、識者の意見が欲しいところです……。

追記

GCC 4.6/4.7 では decltype の挙動が FDIS での修正に沿っていないようで、conditional operator の結果に対する decltype は lvalue の場合 T&(左辺値参照)として、それ以外の場合 T(非参照)として返ってくるようです。その結果 common_type も T(非参照)となり問題は発生しません。

追記2

declval<int>()が抜けていたので訂正しました。

追記3

LWG 2141で同様の指摘が挙げられ修正が決まったようです。

【C++11】 constexpr const

constexpr 楽しいです(挨拶)。先進的な C++er の方々は既に constexpr をばりばり使われていたわけですが「あぁ constexpr、すごいですよねー」みたいな他人事的な感覚でした。が、自分で書いてみると結構楽しいですね。そんなこんなでちょこちょこと書いていたわけですが constexpr const T* func() や constexpr const T& func() ではなく constexpr const T func() という無駄じゃないんですかそれは、というケースでも意味のあるケースが出てきたので書いてみます。

#include <utility>

template<typename T>
struct wrapper
{
 constexpr wrapper() : t{} {}
 constexpr wrapper(const T& t) : t{t} {}
 constexpr wrapper(const wrapper &t) : t{t.t} {}
 wrapper(wrapper &&t) : t{std::move(t.t)} {}
// GCC 4.7 can compile in this case...
// constexpr wrapper(wrapper &&t) : t{std::move(t.t)} {}
 T t;
};

template<typename T>
constexpr wrapper<wrapper<T>> makeT(T t)
{
 return wrapper<wrapper<T>>(wrapper<T>(t));
}

template<typename T>
constexpr const wrapper<wrapper<T>> makeCT(T t)
{
 return wrapper<wrapper<T>>(wrapper<T>(t));
}

int main(void)
{
// Can't compile GCC 4.6/4.7
// constexpr auto p1 = makeT<int>(1).t;
 constexpr auto p2 = makeCT<int>(2).t;
 return 0;
}

なお GCC 4.5 は literal type に対する解釈が違うのでそもそも通りません。const 無い場合は、move constructor が呼ばれて constexpr じゃねぇと怒られます。結局 move を抑止するための const なのですが constexpr 文脈内なら最初っから const 扱いしてくれてもいいんじゃないの?という気はします。規格的にどうなのかも良く分かりませんが。なお、GCC 4.7 だと move constructor に constexpr を付けるとコンパイルできます。もう何が正しいのか良く分かりません。

2011年12月24日土曜日

はてブボタンの追加と Tweet カウントの表示 on Blogger

以下を実施。

  • はてブボタンの追加
  • Tweet カウントの表示(Tweet ボタンの Twitter 公式化)
  • レイアウト調整

↓ diff の位置、行数は正しくない。上側は </head>の直前に追加。前のコンテキストは SyntaxHighlighter 用追加分なので実際にはテンプレートにはない。下側は改行を追加していてテンプレートでは1行になっている。

はてブ公式の Blogger へのボタン追加users 表示追加の説明は古いので適当に修正。他のアイコンと同じように CSS スプライトを使って hover で色付けというのもちらっと考えたが、Google+ のアイコンだけがカラーになるのが気に入らないのでやめ(狭量)。

Twitter 側は元のボタンを殺して公式ボタンを追加。デフォルトテキストはブログタイトル(「や」の字)と記事タイトルが入るようにしている。

後は、上にずれたり、折り返されたりするので、CSS による強制レイアウト調整。

更新(2012/01/01)

Chrome でしか位置が合っていなかったので、IE、Firefox、Opera でも合うように修正。

diff --git a/blogger-template.xml b/blogger-template.xml
--- a/blogger-template.xml
+++ b/blogger-template.xml
@@ -2,7 +2,6 @@
 <!DOCTYPE html>
 <html b:version='2' class='v2' expr:dir='data:blog.languageDirection' xmlns='http://www.w3.org/1999/xhtml' xmlns:b='http://www.google.com/2005/gml/b' xmlns:data='http://www.google.com/2005/gml/data' xmlns:expr='http://www.google.com/2005/gml/expr'>
   <head>
-    <meta content='IE=EmulateIE7' http-equiv='X-UA-Compatible'/>
     <b:if cond='data:blog.isMobile'>
       <meta content='width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0' name='viewport'/>
     <b:else/>
@@ -669,7 +662,39 @@
  SyntaxHighlighter.config.bloggerMode = true;
  SyntaxHighlighter.all();
 </script>
 <!--  end  syntax highlighter -->
+<!-- begin Hatena button -->
+<style type='text/css'><!--
+.hatena-button {
+  height: 20px;
+}
+.hatena-button-image {
+  vertical-align: middle;
+}
+.hatena-users-image {
+  vertical-align: middle;
+}
+.share-button {
+  vertical-align: middle;
+}
+--></style>
+<!--  end  Hatena button -->
+<!-- begin Twitter button -->
+<script src='http://platform.twitter.com/widgets.js' type='text/javascript'/>
+<style type='text/css'><!--
+iframe.twitter-share-button {
+  vertical-align: middle;
+  width: 90px !important;
+}
+--></style>
+<!--  end  Twitter button -->
+<!-- begin adjust Google+ -->
+<style type='text/css'><!--
+div.dummy-container {
+  width: 220px;
+}
+--></style>
+<!--  end  adjust Google+ -->
 
   </head>
 
@@ -920,7 +944,7 @@
   <div class='clear'/>
 </b:includable>
 <b:includable id='shareButtons' var='post'>
   <b:if cond='data:top.showEmailButton'><a class='goog-inline-block share-button sb-email' expr:href='data:post.sharePostUrl + &quot;&amp;target=email&quot;' expr:title='data:top.emailThisMsg' target='_blank'><span class='share-button-link-text'><data:top.emailThisMsg/></span></a></b:if><b:if cond='data:top.showBlogThisButton'><a class='goog-inline-block share-button sb-blog' expr:href='data:post.sharePostUrl + &quot;&amp;target=blog&quot;' expr:onclick='&quot;window.open(this.href, \&quot;_blank\&quot;, \&quot;height=270,width=475\&quot;); return false;&quot;' expr:title='data:top.blogThisMsg' target='_blank'><span class='share-button-link-text'><data:top.blogThisMsg/></span></a></b:if>
-  <b:if cond='data:top.showTwitterButton'><a class='goog-inline-block share-button sb-twitter' expr:href='data:post.sharePostUrl + &quot;&amp;target=twitter&quot;' expr:title='data:top.shareToTwitterMsg' target='_blank'><span class='share-button-link-text'><data:top.shareToTwitterMsg/></span></a></b:if>
+  <!-- disabled for Twitter own button <b:if cond='data:top.showTwitterButton'><a class='goog-inline-block share-button sb-twitter' expr:href='data:post.sharePostUrl + &quot;&amp;target=twitter&quot;' expr:title='data:top.shareToTwitterMsg' target='_blank'><span class='share-button-link-text'><data:top.shareToTwitterMsg/></span></a></b:if> -->
   <b:if cond='data:top.showFacebookButton'><a class='goog-inline-block share-button sb-facebook' expr:href='data:post.sharePostUrl + &quot;&amp;target=facebook&quot;' expr:onclick='&quot;window.open(this.href, \&quot;_blank\&quot;, \&quot;height=430,width=640\&quot;); return false;&quot;' expr:title='data:top.shareToFacebookMsg' target='_blank'><span class='share-button-link-text'><data:top.shareToFacebookMsg/></span></a></b:if><b:if cond='data:top.showOrkutButton'><a class='goog-inline-block share-button sb-orkut' expr:href='data:post.sharePostUrl + &quot;&amp;target=orkut&quot;' expr:title='data:top.shareToOrkutMsg' target='_blank'><span class='share-button-link-text'><data:top.shareToOrkutMsg/></span></a></b:if>
+  <!-- begin AddHatena -->
+    <a class='goog-inline-block hatena-button' expr:href='&quot;http://b.hatena.ne.jp/entry/add/&quot; + data:post.url'>
+      <img class="hatena-button-image" src="画像URL" width="16" height="12" alt="このエントリーをはてなブックマークに追加" title="このエントリーをはてなブックマークに追加" />
+    </a>
+   
+    <a class='goog-inline-block hatena-button' expr:href='&quot;http://b.hatena.ne.jp/entry/&quot; + data:post.url'>
+      <img class="hatena-users-image" 
+       expr:src='&quot;http://b.hatena.ne.jp/entry/image/&quot; + data:post.url'
+       expr:alt='&quot;はてなブックマーク - &quot; + data:post.title'
+       expr:title='&quot;はてなブックマーク - &quot; + data:post.title' />
+    </a>
+  <!-- end AddHatena -->
+  <!-- begin Twitter button -->
+    <a href="https://twitter.com/share" class="twitter-share-button"
+     expr:data-url='data:post.url' expr:data-text='data:blog.title + &quot; &quot; + data:post.title'>
+      <data:top.shareToTwitterMsg/>
+    </a>
+  <!-- end Twitter button -->
   <b:if cond='data:top.showDummy'><div class='goog-inline-block dummy-container'><data:post.dummyTag/></div></b:if>
 </b:includable>
 <b:includable id='threaded_comment_js' var='post'>
   <script defer='defer' expr:src='data:post.commentSrc' type='text/javascript'/>

2011年12月23日金曜日

【C++11】 list-initialization のオーバーロード解決が分からない

当初は、@cpp_akira さんの記事[C++] 波カッコ初期化のススメにかこつけて波カッコ初期化ができないケースについて書こうと思ったのですが、確認のために調べていたら何が正しいのか分からなくなりましたので、その内容を書いてみます。

#include <initializer_list>
struct A
{
 A(std::initializer_list<int>) {} // #1
 A(int n, double d) {}            // #2
};
A a{ 1, 2.5 }; // tries to call #1 but failed, or calls #2?

n3291 13.3.1.7 Initialization by list-initialization p1 には次のような記述があります。

When objects of non-aggregate class type T are list-initialized (8.5.4), overload resolution selects the constructor in two phases:
  • Initially, the candidate functions are the initializer-list constructors (8.5.4) of the class T and the argument list consists of the initializer list as a single argument.
  • If no viable initializer-list constructor is found, overload resolution is performed again, where the candidate functions are all the constructors of the class T and the argument list consists of the elements of the initializer list.
If the initializer list has no elements and T has a default constructor, the first phase is omitted. In copy-list-initialization, if an explicit constructor is chosen, the initialization is ill-formed.

つまり initializer-list constructor だけを対象としてオーバーロード解決が行われ、viable function が見つからなかった場合に再度全コンストラクタに対してオーバーロード解決が行われます。リストが空の場合はデフォルトコンストラクタが呼ばれます。では先の例はどうなるんでしょうか。Overview of the New C++ (C++11)には次のような例があります。

class Widget {
public:
    Widget(double value, double uncertainty);           // #1
    Widget(std::initializer_list<std::string> values);  // #2
};
double d1, d2;
Widget w1 { d1, d2 }; // tries to call #2; fails because
                      // no double ⇒ string conversion

しかしこれに対しては stackoverflow での質問 C++0x: Overload Resolution規格の最新の変更が未反映との記述があります(Edit-2の所)。で、その質問にもある通り n3291 8.5.4p3 には次のような例があります。

struct S {
 S(std::initializer_list<double>); // #1
 S(const std::string&); // #2
// ...
};
const S& r1 = { 1, 2, 3.0 }; // OK: invoke #1
const S& r2 { "Spinach" }; // OK: invoke #2

これから考えると最初の例でも #2 を呼んで良い気はするのですが、しかしそれは narrowing conversion が #1 を viable にしないならば、です。これに対するコメントが http://lists.cs.uiuc.edu/pipermail/cfe-commits/Week-of-Mon-20110530/042586.html http://lists.cs.uiuc.edu/pipermail/cfe-commits/Week-of-Mon-20110530/042590.html に書かれています。オーバーロード解決で定数式であるかまたその値を確認するの?、あるいは相反してそうな関連する規格の記述について。うーん、どうなんでしょうか。ちなみに g++ 4.6/4.7 ではエラー、警告が出ます。

追記(2011/12/23)

上の ML (llvm の c front-end の commit メールみたいです) の同じスレッド内で解決していたみたいです。http://www.mail-archive.com/cfe-commits@cs.uiuc.edu/msg35510.html(こっちの方がスレッド見やすそうなのでアーカイブの場所を変えました) で URL が挙げられている n2640 Initializer Lists — Alternative Mechanism and Rationale (v. 2) の {}-lists and narrowing では

We propose that if — after deduction, overload resolution, etc. is done — a narrowing conversion (in the sense of N2531) occurs on a {}-enclosed value, the program is ill-formed. This is subtly different from N2531 in that it doesn't affect the existing overload resolution rules.

と記述されており、narrowing conversion の確認はオーバーロード解決の「後」となっています。ということで最初の例では narrowing ですが implicit な conversion があるため #1 が viable function として残り、全コンストラクタに対する再度のオーバーロード解決は行われません。結果として #1 がオーバーロード解決の結果として選択され、implicit narrowing を含むためエラー(ill-formed)となります。一方、Overview of the New C++ (C++11)、及び n3291 の例では implicit conversion が存在しないため initializer-list constructor が viable function とならず全コンストラクタに対する再度のオーバーロード解決が行われることになります。なお、ML の commit 対象のコードも修正されており、std::initializer_list を引数にとる initializer-list constructor を持つ class (template) A に対して

     // Narrowing conversions don't affect viability. The next two choose
     // the initializer_list constructor.
     { A<3> a{1, 1.0}; } // expected-error {{narrowing conversion}}
     { A<3> a = {1, 1.0}; } // expected-error {{narrowing conversion}}

と、いう風に記述されています。

2011年12月21日水曜日

forward_as_tuple() からの復元

C++11 の <tuple> には forward_as_tuple() という関数があります。

template<class... Types>
std::tuple<Types&&...> forward_as_tuple(Types&&... t) noexcept
{
 return std::tuple<Types&&...>(std::forward<Types>(t)...);
}
与えられた引数を(転送用に) tuple としてまとめる関数ですがまとめた後にどう使えばいいのか悩んでいました。標準ライブラリの中にはこれと逆の変換、tuple から引数の列に戻すものがないように思えたからです。作ろうにも単純に考えると 0,1,...,N-1 のような parameter pack を作る必要がありその方法が思いつきませんでした。std::tuple の Fusion アダプタを書いて Boost.Fusion の invoke() に回してみたこともあります。ところが Boost 勉強会 #7 の配信を見ていたところ、ボレロ村上さん(@bolero_MURAKAMI)による 中3女子でもわかるconstexpr で index_tuple イディオムが紹介されていました。まさしく 0,1,...,N-1 のような parameter pack を作成する方法です。ということで tuple を引数に展開して関数を呼び出すヘルパを書いてみました。g++ 4.6/4.7 でコンパイルできます。
#include <tuple>
#include <iostream>

// index_tuple 用クラス

template<std::size_t ... args>
struct indices
{
};

template<std::size_t val, typename T>
struct make_indices_impl
{
// I'm not sure that this (dependent value postpones static_assert) is correct
 static_assert(!std::is_same<T, T>::value, "internal error: make_indices_impl instansiated with invalid arguments");
};

template<std::size_t val, std::size_t ... args>
struct make_indices_impl<val, indices<args...>>
{
 typedef indices<val, args...> type;
};

template<std::size_t beg, std::size_t end>
struct make_indices
{
 static_assert(beg < end, "invalid range");
 typedef typename make_indices_impl<beg, typename make_indices<beg+1, end>::type>::type type;
};

template<std::size_t beg>
struct make_indices<beg, beg>
{
 typedef indices<> type;
};

// tuple を展開して関数を呼び出すヘルパ関数

template<typename F, typename T, std::size_t ... args>
constexpr auto call_imp(F f, T t, indices<args...> i) -> decltype(f(std::get<args>(t)...))
{
 return f(std::get<args>(t)...);
}

template<typename F, typename T>
constexpr auto call(F f, T t) -> decltype(call_imp(f, t, std::declval<typename make_indices<0, std::tuple_size<T>::value>::type>()))
{
 typedef typename make_indices<0, std::tuple_size<T>::value>::type indices_type;
 return call_imp(f, t, indices_type());
}

// テスト用関数

constexpr int sum(int n1, int n2, int n3)
{
 return n1 + n2 + n3;
}

constexpr int func() { return 2; }

int main(void)
{
 int n = 0;
 std::cout << call(sum, std::forward_as_tuple(n, 1, func())) << std::endl;
 std::cout << call(sum, std::forward_as_tuple(1, 2, 3)) << std::endl;
 return 0;
}
gcc 4.6/4.7 の libstdc++ では拡張として tuple も constexpr 化されているため↑コードの関数テンプレートに constexpr をつけることができます。なお、-O2 ではなく -O でも call() の呼び出しが定数(それぞれ 3, 6)に展開されていました。

2011年12月18日日曜日

リンク:アムダールの法則


  • The Law According to Amdahl
    アムダールの法則が何かで何に適用できるのかは置いといて一般論として「最適化して得られるのはその最適化した領域が全体に与えている割合以上に大きくならない」と捉えればいいよね、という記事。
    最適化については「するな」「まだするな」とか警句が多いですね。
  • Parallel Programming: When Amdahl’s law is inapplicable?
    アムダールの法則として参照される数式について、種々の仮定に基づくものであり特に直列部分が常時クリティカルパス上に乗っている場合のものだから、それにそぐわない状況下(現在の並列化パラダイムの多く)でアムダールの法則って言うんじゃないよ、という記事。
  • Parallel Programming: Amdahl’s Law or Gustafson’s Law
    ↑の補足としてグスタフソンの法則について。アムダールの法則として参照される数式について置かれている仮定に加えて、問題の規模が大きくなり続け、かつ、直列部分の割合が小さくなる場合がグスタフソンの法則で、この仮定が成立しないならグスタフソンの法則も成立しないし、いずれにしろ直列部分を小さくしろ、というのは変わらないという記事。

【Boost Advent Calendar 2011】 18日目 Boost.ICL の紹介

本記事は Boost Advent Calendar 2011 18日目の記事となります。

前振り

C++ の魅力の一つとしてライブラリによってあたかも言語機能が拡張されたかのようなことを実現できる、というものがあると思います。Boost Advent Calendar を読まれる/書かれるような方もそういった言語機能寄りの部分に興味を持たれている方が多いのかなぁ、という印象を持っていたりします。そうした中でそれなりに応用寄りでありながら取り上げられる機会が多いライブラリとして Boost.Graph が挙げられます。これは多くの問題がグラフの問題に帰着され応用寄りといいつつ広く使えるライブラリであること、C++ 的な generic なデザインであることが理由ではないかと思います。Boost.ICL (Interval Container Library)も応用寄りでありながら広く使えそうなライブラリで、かつ、C++ らしいライブラリだと思うのですが、なぜか記事が少ないようです。ということで僭越ながら(自分だってほとんど使ってなかったりしますが)簡単な紹介を書いてみたいと思います。

で ICL って何?

今ひとつ取り上げられない ICL ですが、その理由の一つとして名前が分かりにくい、という点が挙げられると思います(ドキュメントが数学寄りってのもある気がします)。Spirit や Wave のように内容を表現することを初めから放棄している変態ライブラリではないにも関わらず ICL だとさっぱり分からず Interval Container Library でもやっぱり良く分からないわけです。

interval とは区間のことです。Boost には別に Interval というライブラリもありこちらは区間演算を対象としたライブラリです(参考:Boost.勉強会 #7 東京での @pepshiso さんの発表資料 Boost.Intervalで区間演算)。しかし ICL は Interval とは直接の関係はありません。interval では単一の区間に対する数値演算を対象としていましたが、ICL では区間の集合を取り扱います。

まだ良く分かりませんね。こうした区間(の集合)について最も使われやすいのは時間でしょう。カレンダーなり手帳なりそこにはスケジュールが書いてあると思います。何時から何時まで予定、何時から何時まで予定。そう、これが区間の集合です。

それでは、予定のリストから順に衝突しない予定を登録していく、というコードを書いてみましょう。

#include <boost/icl/interval_set.hpp>

// Date_Time の posix_time とアダプタコード
#include <boost/icl/ptime.hpp> 

#include <string>
#include <iostream>

std::string requests[][2] = {
 { "2011-12-18 15:00", "2011-12-18 16:00" },
 { "2011-12-18 14:00", "2011-12-18 15:30" },
 { "2011-12-18 10:00", "2011-12-18 12:00" },
 { "2011-12-18 14:00", "2011-12-18 15:00" },
 { "2011-12-18 13:00", "2011-12-18 14:00" },
};

int main(void)
{
 using boost::posix_time::ptime;
 using boost::posix_time::time_from_string;
 
 using boost::icl::interval_set;
 using boost::icl::discrete_interval;

 interval_set<ptime> reserved;

 for(std::size_t i = 0; i < sizeof(requests) / sizeof(requests[0]); ++i) {
  discrete_interval<ptime> request = discrete_interval<ptime>::right_open(
    time_from_string(requests[i][0]),
    time_from_string(requests[i][1])
  );
  if(disjoint(reserved, request)) {
   reserved += request;
   std::cout << "RESERVED:     " << request << std::endl;
   std::cout << "STATUS:       " << reserved << std::endl;
  } else {
   std::cout << "NOT RESERVED: " << request << std::endl;
  }
 }

 return 0;
}
ほぼ自明なコードだと思います。interval_set が区間の集合、連続値ではなく離散値であるため discrete_interval を使っています。right_open は右開区間、left <= x < right な区間の作成です。disjoint により予定が衝突しているかを判別し、+= によって区間を集合に追加しています。出力結果は以下の通り。
RESERVED:     [2011-Dec-18 15:00:00,2011-Dec-18 16:00:00)
STATUS:       {[2011-Dec-18 15:00:00,2011-Dec-18 16:00:00)}
NOT RESERVED: [2011-Dec-18 14:00:00,2011-Dec-18 15:30:00)
RESERVED:     [2011-Dec-18 10:00:00,2011-Dec-18 12:00:00)
STATUS:       {[2011-Dec-18 10:00:00,2011-Dec-18 12:00:00)[2011-Dec-18 15:00:00,2011-Dec-18 16:00:00)}
RESERVED:     [2011-Dec-18 14:00:00,2011-Dec-18 15:00:00)
STATUS:       {[2011-Dec-18 10:00:00,2011-Dec-18 12:00:00)[2011-Dec-18 14:00:00,2011-Dec-18 16:00:00)}
RESERVED:     [2011-Dec-18 13:00:00,2011-Dec-18 14:00:00)
STATUS:       {[2011-Dec-18 10:00:00,2011-Dec-18 12:00:00)[2011-Dec-18 13:00:00,2011-Dec-18 16:00:00)}
14:00-15:00 を追加したときに interval_set 中で 15:00-16:00 と区間が統合されて 14:00-16:00 となっているのが見てとれます。この挙動は集合の型を変えることにより変更が可能です。interval_set の代わりに separate_interval_set を使った場合、出力は以下のようになります。
RESERVED:     [2011-Dec-18 15:00:00,2011-Dec-18 16:00:00)
STATUS:       {[2011-Dec-18 15:00:00,2011-Dec-18 16:00:00)}
NOT RESERVED: [2011-Dec-18 14:00:00,2011-Dec-18 15:30:00)
RESERVED:     [2011-Dec-18 10:00:00,2011-Dec-18 12:00:00)
STATUS:       {[2011-Dec-18 10:00:00,2011-Dec-18 12:00:00)[2011-Dec-18 15:00:00,2011-Dec-18 16:00:00)}
RESERVED:     [2011-Dec-18 14:00:00,2011-Dec-18 15:00:00)
STATUS:       {[2011-Dec-18 10:00:00,2011-Dec-18 12:00:00)[2011-Dec-18 14:00:00,2011-Dec-18 15:00:00)[2011-Dec-18 15:00:00,2011-Dec-18 16:00:00)}
RESERVED:     [2011-Dec-18 13:00:00,2011-Dec-18 14:00:00)
STATUS:       {[2011-Dec-18 10:00:00,2011-Dec-18 12:00:00)[2011-Dec-18 13:00:00,2011-Dec-18 14:00:00)[2011-Dec-18 14:00:00,2011-Dec-18 15:00:00)[2011-Dec-18 15:00:00,2011-Dec-18 16:00:00)}
隣接した区間でも統合されていないのが分かります。ちなみに right_open の代わりに closed (閉区間) を使うと(interval_set か separate_interval_set かに関わらず)出力はこうなります。閉区間なので端だけが衝突している場合でも登録できなくなります。
RESERVED:     [2011-Dec-18 15:00:00,2011-Dec-18 16:00:00]
STATUS:       {[2011-Dec-18 15:00:00,2011-Dec-18 16:00:00]}
NOT RESERVED: [2011-Dec-18 14:00:00,2011-Dec-18 15:30:00]
RESERVED:     [2011-Dec-18 10:00:00,2011-Dec-18 12:00:00]
STATUS:       {[2011-Dec-18 10:00:00,2011-Dec-18 12:00:00][2011-Dec-18 15:00:00,2011-Dec-18 16:00:00]}
NOT RESERVED: [2011-Dec-18 14:00:00,2011-Dec-18 15:00:00]
RESERVED:     [2011-Dec-18 13:00:00,2011-Dec-18 14:00:00]
STATUS:       {[2011-Dec-18 10:00:00,2011-Dec-18 12:00:00][2011-Dec-18 13:00:00,2011-Dec-18 14:00:00][2011-Dec-18 15:00:00,2011-Dec-18 16:00:00]}
今度は2人の予定から(就業時間中に)両方が開いている時間帯を求めるイメージのコードです。
#include <boost/icl/interval_set.hpp>

// Date_Time の posix_time とアダプタコード
#include <boost/icl/ptime.hpp> 

#include <string>
#include <iostream>

std::string worktime[][2] = {
 { "2011-12-18 08:30", "2011-12-18 12:00" },
 { "2011-12-18 13:00", "2011-12-18 17:30" },
};

std::string schedule1[][2] = {
 { "2011-12-18 09:00", "2011-12-18 10:30" },
 { "2011-12-18 11:00", "2011-12-18 12:00" },
 { "2011-12-18 15:00", "2011-12-18 16:00" },
 { "2011-12-18 17:00", "2011-12-18 21:00" },
};

std::string schedule2[][2] = {
 { "2011-12-18 08:30", "2011-12-18 11:00" },
 { "2011-12-18 13:00", "2011-12-18 14:00" },
 { "2011-12-18 15:00", "2011-12-18 16:00" },
};

template<typename T, std::size_t N>
void read(T& t, std::string (&spec)[N][2])
{
 using boost::posix_time::ptime;
 using boost::posix_time::time_from_string;
 using boost::icl::discrete_interval;

 for(std::size_t i = 0; i < N; ++i) {
  discrete_interval<ptime> duration = discrete_interval<ptime>::right_open(
    time_from_string(spec[i][0]),
    time_from_string(spec[i][1])
  );
  t += duration;
 }
}

int main(void)
{
 using boost::posix_time::ptime;
 using boost::icl::interval_set;

 interval_set<ptime> available, reserved1, reserved2;

 read(available, worktime);
 read(reserved1, schedule1);
 read(reserved2, schedule2);

 std::cout << (available - reserved1 - reserved2) << std::endl;

 return 0;
}
{[2011-Dec-18 14:00:00,2011-Dec-18 15:00:00)[2011-Dec-18 16:00:00,2011-Dec-18 17:00:00)}
集合演算で正味1行です。(available - (reserved1 | reserved2)) でも同じ結果になります。なお今までの所、interval_set 全体を単純にストリーム出力していますが普通にコンテナなので begin(), end() でループも回せますし algorithm にも渡せます。

map もあるよ!

ここまででも十分使い道のあるライブラリだと言えると思いますがもうひとつ特筆すべきなのは map の存在です。当初 ICL を見ていた時「set は分かる、しかし map ってどうなんねん」と思ったものでした。区間が重複しない場合は特に問題はありませんが区間が重複している場合はどう処理するのか。答えは処理を選択可能である、です。数値に対するデフォルトでは追加、削除によって値を+-します。ということで、ある時間帯に何人が来るかというデータから最大人数になる時間帯を求めてみましょう。ただし人数が同じ時間帯がある場合は、最も長い時間帯を、長さも同じ場合は開始時間が早い時間帯とします。
#include <boost/icl/interval_map.hpp>

// Date_Time の posix_time とアダプタコード
#include <boost/icl/ptime.hpp> 

#include <boost/lambda/lambda.hpp>

#include <string>
#include <iostream>
#include <algorithm>
#include <utility>

// 入力データ用型
struct spec
{
 std::string from, to;
 int count;
};

// 入力データ
spec input[] = {
 { "2011-12-18 14:30", "2011-12-18 15:30", 4 },
 { "2011-12-18 11:00", "2011-12-18 18:00", 1 },
 { "2011-12-18 10:00", "2011-12-18 16:00", 3 },
 { "2011-12-18 13:00", "2011-12-18 15:00", 2 },
 { "2011-12-18 12:30", "2011-12-18 13:30", 4 },
};

// typedef
typedef boost::icl::interval_map<boost::posix_time::ptime, int> map_type;
typedef map_type::value_type value_type;
typedef map_type::iterator iterator;

// 読み込み用ヘルパ
template<std::size_t N>
void read(map_type& t, spec (&specs)[N])
{
 using boost::posix_time::ptime;
 using boost::posix_time::time_from_string;
 using boost::icl::discrete_interval;

 for(std::size_t i = 0; i < N; ++i) {
  discrete_interval<ptime> duration = discrete_interval<ptime>::right_open(
    time_from_string(specs[i].from),
    time_from_string(specs[i].to)
  );
  t += std::make_pair(duration, specs[i].count);
 }
}

// 比較用関数オブジェクト
// 1) 人数が小さい方
// 2) 人数が同じ場合は、期間が短い方
// 3) 人数、期間が同じ場合は、開始時間が遅い方
struct compare
{
 bool operator()(const value_type &v1, const value_type &v2) const
 {
  const value_type::first_type & key1 = v1.first;
  const value_type::first_type & key2 = v2.first;
  const value_type::second_type & value1 = v1.second;
  const value_type::second_type & value2 = v2.second;

  return value1 <  value2 ||
    (value1 == value2 && key1.upper() - key1.lower() <  key2.upper() - key2.lower()) ||
    (value1 == value2 && key1.upper() - key1.lower() == key2.upper() - key2.lower() && 
   key1.lower() > key2.lower());
 }
};

int main(void)
{
 map_type sum;

 read(sum, input);
 iterator it = max_element(sum.begin(), sum.end(), compare());
 std::cout << it->first << ':' << it->second << std::endl;

 return 0;
}
出力結果と図示は以下のイメージになります。
[2011-Dec-18 13:00:00,2011-Dec-18 13:30:00):10
単純に区間とそれに紐づけられた値(参加人数)を += していき基準に合わせて max_element を呼んでいるだけです。前述の通り、紐づけられた値が数値なのでデフォルトでは + で集計されますが、例えば interval_map<key, set<T> > だったりすると和集合、差集合になりますし、interval_map の第 5 template 引数に functor を渡してやることでカスタマイズも可能です。例えば inplace_max を渡すと最大値をとるようになったりします。
#include <boost/icl/interval_map.hpp>

// Date_Time の posix_time とアダプタコード
#include <boost/icl/ptime.hpp> 

#include <boost/lambda/lambda.hpp>

#include <string>
#include <iostream>
#include <algorithm>
#include <utility>

// 入力データ用型
struct spec
{
 std::string from, to;
 int count;
};

// 入力データ
spec input[] = {
 { "2011-12-18 14:30", "2011-12-18 15:30", 4 },
 { "2011-12-18 11:00", "2011-12-18 18:00", 1 },
 { "2011-12-18 10:00", "2011-12-18 16:00", 3 },
 { "2011-12-18 13:00", "2011-12-18 15:00", 2 },
 { "2011-12-18 12:30", "2011-12-18 13:30", 4 },
};

// typedef
typedef boost::icl::interval_map<boost::posix_time::ptime, int,
                                 boost::icl::partial_absorber, std::less,
                                 boost::icl::inplace_max> map_type;
typedef map_type::iterator iterator;

// 読み込み用ヘルパ
template<std::size_t N>
void read(map_type& t, spec (&specs)[N])
{
 using boost::posix_time::ptime;
 using boost::posix_time::time_from_string;
 using boost::icl::discrete_interval;

 for(std::size_t i = 0; i < N; ++i) {
  discrete_interval<ptime> duration = discrete_interval<ptime>::right_open(
    time_from_string(specs[i].from),
    time_from_string(specs[i].to)
  );
  t += std::make_pair(duration, specs[i].count);
 }
}

int main(void)
{
 map_type peak;

 read(peak, input);
 for(iterator it = peak.begin(); it != peak.end(); ++it)
 {
  std::cout << it->first << " : " << it->second << std::endl;
 }
 return 0;
}
出力結果と図示は以下のイメージになります。
[2011-Dec-18 10:00:00,2011-Dec-18 12:30:00) : 3
[2011-Dec-18 12:30:00,2011-Dec-18 13:30:00) : 4
[2011-Dec-18 13:30:00,2011-Dec-18 14:30:00) : 3
[2011-Dec-18 14:30:00,2011-Dec-18 15:30:00) : 4
[2011-Dec-18 15:30:00,2011-Dec-18 16:00:00) : 3
[2011-Dec-18 16:00:00,2011-Dec-18 18:00:00) : 1

まとめ

ほんのさわりですが、ICL について簡単に紹介しました。元々 ICL はドイツの Cortex Software GmbH が病院情報システムの開発をする際に開発した Interval Template Library を Boost に提案したものです。出自からも実用性は保証済みと言っても良いでしょう。こうした区間に対する処理は情報処理システムではどこにでも出てくるようなものです。皆さんも ICL を使ってみてはいかがでしょうか?
これで Boost Advent Calendar 2011 18 日目は終了です。19 日目は id:phi16(@phi16_) さん担当になります。

2011年12月17日土曜日

【C++11 Advent Calendar 2011】17日目 小ネタ集 後編

本記事は C++11 Advent Calendar 17日目の記事です。前編だけだとネタが薄いんじゃないかと思って簡単に調べたネタになります。
さて、Advent Calendar やられるくらい C++11 さんは期待されているわけですが、一方の C99 さんのネグレクトされ具合は割とひどいです。が、大丈夫。C++11 では C99(というより次期規格の C1X)との互換性が向上しています。この後編では C++11 に取り込まれた C99 の機能を簡単に眺めてみましょう。後編においても他人のふんどしで相撲をとるべく seclan@dll.jp さんによるプログラミング言語 C の新機能の構成に合わせて比較していきます。なお、基本的に俺調べとなりますので信頼度は低いです。
  • 1 概略
    • 1.2 新しい予約語 (n3291 2.12)
      inline は既存、restrict は無し、_Bool, _Complex は代替が既に存在するのでなし、_Imagnary はな し
    • 1.3 新しいヘッダファイル (n3291 17.6.1.2)
      <choge> 形式を加えて C99 で追加されているものは全て追加されています。なお、<cstdalign>、<cuchar> は C1X との同期です(<cuchar> は C Unicode TR こと ISO/IEC TR 19769:2004 が参照文書になっています)。
  • 2 C プリプロセッサ
    • 2.1 新しいコメント表記方法
      // のことなので既存です。
    • 2.2 空引数を許された関数型マクロ呼出し (n3291 16.3/4)
      マクロで a(3,,5) とかが書けます。追加されました。
    • 2.3 可変個数引数を持つマクロ定義 (n3291 16.3/4,5, 16.3.1)
      __VA_ARGS__ のことです。DigitalGhost さんの記事を参照。
    • 2.4 あらかじめ定義されているマクロ名 (n3291 16.8)
      • __STDC_VERSION__
        C 言語仕様のバージョンですが定義の有無から実装依存です。
      • __STDC_ISO_10646__
        定義されている場合には wchar_t がいつまでの ISO/IEC 10646 (Unicode 相当)に従っているかを表す日付になります。
      • __STDC_HOSTED__
        C99 にもあるので参照元ページでの説明漏れだと思われます。hosted environment かどうかを示します。
      • __STDC_MB_MIGHT_NEQ_WC__
        C1X からです。C++11 では 1 固定。1 だと wchar_t のエンコーディングについて basic charcter set 範囲が char での表現と違っているかもしれないという意味です。
      • __STDC_IEC_559__
        なし?ただし以前より numeric_limits に is_iec559 があります。
      • __STDC_IEC_559_COMPLEX__
        なし?
      • C++11 で追加された定義済みマクロは他にもありますが C との比較の観点なので省略します。
    • 2.5 プログラミング言語 C 標準プラグマ
      一部 #pragma を標準化しようというもので #pragma STDC hogehoge という形式になります。C++11 にはなさそげです。浮動小数点絡みはなくてもいいんでしょうか……
    • 2.6 単項演算子 _Pragma (n3291 16.9)
      追加されました。Flast さんの記事を参照ください。
    • 2.7 プリプロセッサ式における整数型 (n3291 16.1/4)
      intmax_t 型、uintmax_t 型で計算されます。C99 に合わせて変更されています。
    • 2.8 文字列定数とワイド文字列定数の結合 (n3291 2.14.5/13)
      例えば "foo" L"bar" が L"foobar" になります。C99 に合わせて変更されていますが、C++11 では文字列の種類が増えたので拡張されています。
      • 両方が同じ prefix → その prefix のまま
      • 片方が prefix ありで、他方が prefix なし → prefix あり
      • u8 と L が隣接 → ill-formed
      • 他は実装依存
  • 3 字句
  • 4 配列
    • 4.1 可変長配列
      int a[n]; (n は非定数式)とか書けるやつですが C++11 には有りません。
    • 4.2 構造体中の 0 長配列メンバ
      構造体末尾に int a[] とか置いておき malloc() 等でメモリを確保する際に余分に取って a[5] とかでアクセスできるものですが、C++11 にはなさそうです。
    • 4.3 配列要素中の記憶/型修飾子
      int n[static 5] とか書けるそうです。C++11 にはありません。構文的には属性を使って int n[5][[static]]; みたいに書ける処理系があってもいいかもしれません。
  • 5 整数型
    • 5.1 Bool 型
      bool があるので無用です
    • 5.2 long long int 型 (n3291 3.9.1/2,3)
      追加されました。
    • 5.3 long long int 型の定数 (n3291 2.14.2)
      LL とか ULL サフィクスです。追加されました。
    • 5.4 整数除算 (n3291 5.6/4)
      C99 と同じくいずれかのオペランドが負の場合でも 0 方向に切り捨てるよう規定が出来ました。5 / -2 は -2 になります。
  • 6 複素数型
    complex があるので基本省略、……なのですが C99 の hoge_Complex 型とメモリ配置の互換性をとるためでしょう、
    1. z を std::complex<T> として reinterpret_cast<cv T(&)[2]>(z)[0] が実部、reinterpret_cast<cv T(&)[2]>(z)[1] が虚部になること
    2. a を cv std::complex<T>* として、reinterpret_cast<cv T*>(a)[2*i] が a[i] の実部を、reinterpret_cast<cv T*>(a)[2*i + 1] が a[i] の虚部となること
    という規定が <complex> に追加されています。
  • 7 文法一般
  • 8. 標準ライブラリ(拡張)
    C99 とは別の話で余談になりますが、Concurrency 関係でデータ競合についての補足が結構追加されていたりします。
    • 8.1 ctype.h (n3291 21.7)
      C99 同様に isblank() が追加されています。
    • 8.2 float.h (n3291 18.3/3)
      C99 同様に DECIMAL_DIG と FLT_EVAL_METHOD が追加されています。
    • 8.3 math.h (n3291 26.8)
      多いので詳細は省略しますが 
      • C99 で追加されたマクロは同様に追加されています。ただし C99 で追加された関数型マクロは C++11 ではオーバーロード関数になっています。
      • 標準プラグマは前述の通りありません。
      • hogef()、hogel() に相当する hoge() のオーバーロードが(C++98から)あります。
    • 8.4 stdarg.h (n3291 18.10)
      C99 同様 va_copy が追加されています。
    • 8.5 stdio.h (n3291 27.9.2)
      C99 で追加されたものは書式文字列を含めて全て追加されています。snprintf(),vsnprintf(), vscanf() ファミリーも同様です。
    • 8.6 stdlib.h (n3291 18.5, 18.10, 20.6.13, 21.7, 25.5, 26.8)
      C99 同様 _Exit(), strtof(), strtold(), strtoll(), strtoull(), atoll(), llabs(), lldiv(), ldiv_t 型, lldiv_t 型 が追加されています。abs(), div() は long int, long long int に対するオーバーロードも追加されています。
    • 8.7 wchar.h (n3291 21.7)
      C99 同様 vwscanf(), vswscanf(), vfwscanf(), wcstof(), wcstold(), wcstoll(), wcstoull() が追加されています。
    • 8.8 wctype.h (n3291 21.7)
      C99 同様 iswblank() が追加されています。
  • 9. 標準ライブラリ(新規)
    基本的に追加分のヘッダは全て存在しますが、一部変更があります。
    • 9.1 complex.h: 複素数 (n3291 26.4.10)
      単純に <complex> を #include するだけになっています。
    • 9.2 fenv.h: 浮動小数点環境 (n3291 26.3)
      スレッド毎に環境が用意されること、スレッド生成時には親スレッドの環境を引き継ぐことが追加で規定されています。
    • 9.3 inttypes.h (n3291 27.9.2/3,4)
      書式指定用マクロ(PRIdFAST8 とか)は__STDC_FORMAT_MACROSの有無に関わらず常に定義されるようになっています。また、abs(), div() が intmax_t に対してもオーバーロードされます。
    • 9.4 stdbool.h (n3291 18.10/8)
      マクロ bool, true, false は定義されません。__bool_true_false_are_defined だけが定義されます。
    • 9.5 stdint.h (n3291 18.4)
      __STDC_CONSTANT_MACROS, __STD_LIMIT_MACROS に関わらずマクロも定義されます。ちなみに <atomic> で <cstdint> にある全ての型について atomic<T> が特殊化されていることが規定されています。
    • 9.6 tgmath.h: 型総称数学関数 (n3291 26.8/1,2)
      C言語的には驚きの tgmath.h さん(擬似的に数学関数のオーバーロードを実現します)ですが、C++ 的にはオーバーロードがあるので <ccomplex> と <cmath> を単純に #include するだけです。
    • stdalign.h (n3291 18.10/7)
      C1X との同期です。n3291 だと alignas は定義されず __alignas_is_defined だけが定義されるように読めるのですが、alignof はどこに行ったのでしょうか?C1X DIS だと alignof 系もあるようなのですが……
    • uchar.h (n3291 21.7)
      C1X にもありますが規格上は C Unicode TR ISO/IEC TR 19769:2004 を参照文献としています。char16_t, char32_t は C++11 言語側にあるので __STDC_UTF_16__, __STDC_UTF_32__, mbrtoc16(), c16rtomb(), mbrtoc32(), c32rtomb() が定義されています。
  • A. 付録
    • A.1 strftime 書式指定子 (n3291 20.11.8)
      <ctime> は C の time.h と同じだと書いてありますので C99 と同様に拡張されています。"strftime supports the C conversion specifiers C, D, e, F, g, G, h, r, R, t, T, u, V, and z, and the modifiers E and O." と注釈もついています。
    • A.2 printf の書式 (n3291 27.9.2)
      特に規定がないので C99 と同じだと思われます。例えば %lf さんが公式に許可されるほか、std::size_t に対する z (%zu みたいに使う)、std::ptrdiff_t に対する t (%td みたいに使う)、そして定数としては使えないのに16進浮動小数点数としての出力 %a ないし %A などが追加されます。
お疲れ様でした。どうでしょうか。「C に合わせた変更も結構多いな」という印象ではないでしょうか。とりあえず C にあって未だ C++ に無い主なものとしては
  • restrict
  • 標準プラグマ
  • 浮動小数点定数の 16 進数表記
  • 可変長配列
  • 指示付きの初期化子 (Designated Initializer)
辺りでしょうか。ないなら作れ、「『欠・即・産』。これが、Boost に魅入られた C++er が共有した真の正義だったはず」ってことで、浮動小数点定数の 16 進数表記、指示付きの初期化子 (Designated Initializer)辺りはライブラリである程度補完できるかもしれません。Designated Initializer は相当妥協や無理矢理をやらないと厳しそうですが「浮動小数点定数の 16 進数表記」は割とすんなり近いものは書けそうということで簡単にでっちあげてみました。オーバーフロー、エラーチェック、型の考慮、型サフィクス非対応等適当ですが大体できそうというのは見て取れるのではないかと思います。ただし __builtin_scalbn() を使って手を抜いているので GCC 専用です。g++ 4.7.0 20111203 だと user-defined literal で、g++ 4.5/4.6 だと user-defined literal の呼び出しができないのでそこだけ手書きした状態で通ります。例えば下記コードで 31.25 と表示されます。
// for g++ 4.7
#include <iostream>
#include "hexfloat.hpp"

int main(void)
{
    using yak::util::hexfloat::operator "" _hf;

    std::cout << 0xF.ap+1_hf << std::endl;

    return 0;
}
C++11 Advent Calendar 17日目はこれで終了です。明日 18 日目は id:ignisan(@ignis_fatuus) さんです。

追記(2011/12/28)

Fusion さんの助けを借りつつ無理矢理 Designated Initializer っぽいものもでっちあげてみました。http://yak-ex.blogspot.com/2011/12/c11-designated-initializer-boostfusion.html

【C++11 Advent Calendar 2011】17日目 小ネタ集 前編

本記事は C++11 Advent Calendar 17日目の記事です。
C++er 待望の C++11、大きな変更が目白押しでどうしてもその辺りが注目されるわけですがそういうのは偉い先生に任せて今回は軽く小ネタ集で行ってみましょう。
ちなみに私が持っているのは n3291 ですので最終的な規格と違っている可能性があります、悪しからずご了承ください。
さて、発行されたばかりの C++11、書籍もまだ出ていない(と思う)訳ですが、Effective C++ 等で有名な Scott Meyers 先生が C++11 解説コース用の資料を PDF で販売されています(Presentation Materials: Overview of the New C++ (C++11))。前編ではこれをさらっと流し読みした中で、へぇぇと思った点を挙げていきたいと思います。なお、ページ数は cpp11Notes2011-10-25 でのものです。

■ auto と template parameter type deduction (p.22)

auto による型導出と template parameter の型導出とは基本的に同一となりますが
template<typename T> void f(T t);

// 基本的に一緒
f(expr);
auto v = expr;
唯一違う(と Meyers 先生が言っている)のが expr に brace initialization list、つまり { 1, 2, 3 } などが与えられた場合です。auto は std::initializer_list<T> (この場合 T = int)となりますが、template parameter の場合は型導出されません。
gcc 4.5/4.6 の場合はデフォルトでは警告になりますが、4.7 だとエラーになります。

■文字リテラル、文字列リテラルと、UCS-2 / UTF-16 (p.34, 35)

u'x' の場合 x は UCS-2 で表現可能な範囲内(つまり BMP 内)である必要がありますが、u"hoge" の場合、hoge は UTF-16 で表現可能な範囲内(つまり Unicode 全域)でも OK です。

■Implicit narrowing (p.52-)

C++11 で地味に便利な機能として Uniform Initialization があります。簡単に言ってしまえば今まで配列、(コンストラクタに依らない)構造体の初期化は int a[3] = { 1, 2, 3 } のような形式で、クラスの初期化は X x(arg); のような形式で統一されていませんでした。これが統一的に
int a[3] { 1, 2, 3 }; // = 省略可能

X x{arg};
と書けるようになるのですが、一点注意しなければならないのがこの形式の場合 Implicit narrowing を認めないという点です。
struct Point { int x, y; }
Point p1 = { 1, 2.5 }; // 構文的には C++11 では = なくてもよい
これが C++11 で通りません。まぁ、これはまだ感覚的に納得できそうですが実はこれも通りません。
// gcc 4.5 / 4.6 だと通ってしまうが
// gcc 4.7 / clang 3.0 だとエラー
int x = 1;
double d{x};
ただしこれなら通ります。
const int x = 1;
double d{x};
一方(通常の環境では)これは通りません。
const int x = 0x7FFFFFFF;
float d{x}; // float で表現できない
認められるケースは constant expression でかつ変換先で表現可能な範囲の場合に限定されているんですね。なお、元ネタ資料では error: double can’t exactly represent all ints ってコメントが付いてますが表現可能なんじゃないでしょうか。


■const T&& (p.245) / delete (p.294)

「const T&& は合法だけど使い道なんかねーよ」「既に発見されたけどな!(正にC++er)」(意訳)
規格中でも使われています。
template <class T> reference_wrapper<T> ref(T&) noexcept;
template <class T> void ref(const T&&) = delete;
lvalue reference に対して呼び出せるけど rvalue reference に対して呼び出せないようにするためのものです。これが有効なのは deleted な関数は使えないけど宣言はされているためです。従ってコピーコンストラクタを delete した場合、暗黙のコンストラクタ生成等も抑止され以下のコードはコンパイルできません(暗黙のデフォルトコンストラクタがない)。
struct A
{
    A(const A&) = delete;
};
A a;

■explicit operator bool (p.312)

コンストラクタへの explicit 指定は皆さん使っていますね? C++11 では変換演算子に対しても explicit 指定ができるようになりました。が、explicit operator bool だけは特殊で「文脈的に bool に変換されている場合(contextually converted to bool)」明示的な変換を必要としません。unspecified bool type idiom の置き換えを狙ったものでしょう。
struct A { explicit operator bool() const { return false; } } a;
// OK
if(a) { /**/ }         // if, while, do-while, for の条件
int m = !a;            // ! 演算子の引数
int n = a ? 1 : 0;     // 条件演算子の引数
if(a || true) { /**/ } // ||, && 演算子の引数
// 後は static_assert や noexcept 修飾子の引数も暗黙変換可能

// BAD
int o = a; // これは駄目
Effective C++11 とか Exceptional C++11 とかが欲しいところですね。さて、書いてみるとこれだけだとネタが薄いかな(しかも他人のふんどし)ということで、後編では C99 から C++11 に取り込まれた点を見てみます。