2014-10-21

誕生日プレゼントに送られてきたもの

帰宅すると、私宛の誕生日プレゼントとして不思議なものが届いていた

2014年11月号のちゃおである。

ちゃおというのは、主に低年齢を対象とした少女漫画雑誌だ。私の趣味ではないものの、プレゼントとして贈られた以上、義理として中身を確認した。ほとんどのマンガが恋愛物であった。登場人物の男は、いずれも歯の浮くようなセリフばかりそれにしても、少女漫画というのはなぜこんなに無個性で、しかもあまりうまいとは思えない絵柄をしているのだろうか。

なお、ちゃおには付録として、実に使いづらいオモチャのようなボールペンと、粗悪な塩化ビニール製のオモチャのような袋がついていた。

なお、妖怪ハウスに住む女性にちゃおを見せてみたところ、いずれもハイテンションで読みいっていた。やはり、なにか私には理解できぬ女性向けの魅力がこの少女漫画雑誌にはあるらしい。

また同時に、極めて実用的なOD色の靴下が同梱されていた。OD色の靴下が擦り切れて少なくなっていたところであるし、来月は予備自衛官の訓練もあるので、大変ありがたい。

10月25日は筆者の誕生日を記念して、妖怪ハウスでボドゲ会を行う予定だ。

本の虫: 妖怪ハウスでボドゲ会の告知(10/25)

2014-10-pre-Urbanaのレビュー: N4142-N4149

ISO/IEC JTC1/SC22/WG21 - Papers 2014 mailing2014-10

C++標準化委員会の残りの論文はあと100本ほど。年々、発行される文書の数が増えてきている気がする。

[そろそろネタが尽きたがPDF] N4142: Atomic Operations on a Very Large Array

atomic_array<T>の提案

HPC(High Performance Computing)用途で、長大な配列をアトミックに操作したい場合に使える。

atomic_arrayは、すでに確保された配列のアドレスで初期化される。atomic_arrayのオブジェクトを経由して配列にアクセスすれば、その配列は何らかの方法でアトミックに操作できる。atomic_arrayオブジェクトが生存している間は、atomic_array以外の方法で元の配列にアクセスした場合の挙動は未定義。atomic_arrayオブジェクトが破棄された後は、通常通りアクセスできる。

論文にかかれている利用例としては。

  1. 長大な配列をnewやmallocなどの何らかの方法で確保する
  2. シリアル、あるいはパラレルだが競合しない方法で、配列の要素の値を初期化する
  3. 配列をatomic_array<T>オブジェクトでラップする
  4. atomic_array<T>のインターフェースを経由して、パラレルアルゴリズムで配列を書き変える
  5. atomic_array<T>オブジェクトを破棄する
  6. 競合しない方法で配列にアクセスする

atomic_arrayは既存の配列をラップしてアトミックな操作を提供するライブラリで、配列を所有しない。

N4143: Executors and schedulers, revision 4

executorとschedulerに基づく並列実行ライブラリの提案。

リファレンス実装が二つ示されている。

ひとつは、最小限の実装

https://github.com/ccmysen/executors_r4

もうひとつは、タイマーなどの追加的な機能も含む実装

https://github.com/arturl/executors

見方によっては、std::asnyncの高機能版とも言える。

N4144: Searching and Manipulation of Parameter Packs

パラメーターパックを扱うためのライブラリの提案。

パラメーターパックを格納するpacker

using type = std::packer<char, short, int, long> ;

あるpackerに指定した型が含まれるかどうか調べるis_contained_in/is_contains_type

template < typaname ... Types >
void f( Types ... args )
{
    // Typesにint型が含まれているかどうか
    if ( std::is_contained_in_v< int, std::packer< Types ... > > )
        /* ... */ ;

    // Types にint型とdouble型が含まれているかどうか
    if ( std::is_contains_types_v< std::packer< Types ... > , std::packer< int, double > > )
        /* ... */ ;
}

packerの最後に型を付け加えるadd_to、packerに指定した型が含まれていない場合に型を付け加えるunique_add_to

template < typename ... Types >
void f( Types ... args )
{
    // std::packer< types ..., int > 
    using append_int = std::add_to_t< int, std::packer< Types ... > > ;

    // Typesにint型が含まれていない場合のみ追加する
    using append_if_unique = std::unique_add_to_t< int, std::packer< Types ... > > ;
}

型をとりのぞくremove_from、重複した型を取り除くuniqueify


using type = std::packer< char, short, int, short, long > ;

// std::packer< char, int, long >
using removed = std::remove_from_t< short, type > ;

// shortがひとつしか入っていないpacker
// 結果の型の並びは未規定
using unique = std::uniqueify_t< short, type > ;

自前で書くのは面倒なテンプレートメタプログラミングなので、重宝しそうだ。

N4145: Data-Invariant Functions

暗号化処理を実装するにあたって、side-channel attackを防ぐという極めて難しい問題がある。これは、物理的に観測可能実行特性が、秘密のデータによって変わることを利用した攻撃である。たとえば、秘密のデータの値によって、処理時間や電力消費量や、CPUの電圧レギュレーターのノイズなどに変化があり、それを攻撃者が観測できる場合、それらの観測可能な物理的特性によって、秘密のデータを推測することができてしまう。なぜならば、その観測可能な物理的特性の変化は、秘密のデータの値の変化によりもたらされるものだからだ。

opensslの最近のパッチは、一部のif文を、結果の真偽によってタイミングに変化をもたらさないように置き換えるものである。これは将来のコンパイラーの最適化の進化などで無効化されてしまう可能性がある。

この論文では、物理的な実行特性が、入力の値によって変化しないことを保証するライブラリを提案している。このライブラリは、std::constant_time::value<T>の形で使い、比較演算子がオーバーロードされている。

これはなかなか興味深いライブラリの提案だ。

そういえば、最近Hacker Newsでこういう話題が出ていた。

Making sure crypto remains insecure [pdf] | Hacker News

諜報機関が意図的に弱い暗号を使わせるために、TLSやAESのようなサイドチャネル攻撃に弱い暗号を標準化するよう働きかけようという思考実験的なプレゼン資料だ。

N4146: Disposition of Comments, ISO/IEC DIS 14882 C++ 2014

C++14 DISに対する日本からのコメントに対する対応。文面上の細かな指摘が多い。

N4147: Inline variables, or encapsulated expressions

インライン変数の提案。

この論文で提案しているインライン変数とは、変数を表現するオブジェクトを持たず、変数を使った時点で、毎回初期化子が評価される変数である。

int f()
{
    static int count ;
    return ++count ;
}

inline int counter = f() ;

int main()
{
    counter ; // 1
    counter ; // 2
    counter ; // 3
}

つまり、inline変数であるcounterという変数名を使うということは、毎回初期化子である"f()"を評価することと同じ意味になる。もちろん、初期化子を評価した副作用も起こる。また、グローバル変数counterを保持するint型のオブジェクトは存在しないので、メモリを消費しない。

論文では、inline変数によって解決できる様々な問題を列挙している。文法やスコープに従う礼儀正しいマクロや、翻訳単位ごとに定義を書きたいがODR違反は回避したい場合、関数を書くのがあまりにも冗長で面倒な式を複数回使いたい場合などを挙げている。

また、この提案に対する対案として、Expression Aliasなる案も出されたそうだ。これは、例えば、

using name = expression ;

などという文法で、nameを使うと使った場所でexpressionが評価されるというものだ。

それにしても、Future Workで、「ファンクターオブジェクトを使わざる第一級市民たる遅延評価がstd-proposals MLに浮上した。これは関数の引数をinlineと宣言する。評価の意味的には似ている。inline関数には役立つだろう。そういう変数をABI定義されたインターフェースに渡す際には、ポリモーフィックファンクターに変換される。まだ利点は不明なものの、[&]()と書かずに済む」と書かれているのは興味深い。

N4148: Disallowing Inaccessible Operators From Trivially Copyable

コピー/ムーブコンストラクター、コピー/ムーブ代入演算子、デストラクターがdeletedかアクセス不可の場合であっても、torivially copyableの条件に合致してしまう。

// コピーもムーブもしてほしくないクラス
struct X
{
    X( X const & ) = delete ;
    X( X && ) = delete ;
    void operator = ( X const & ) = delete ;
    void operator = ( X && ) = delete ;
} ;

int main()
{
    std::is_trivially_copyable_v< X > ; // true
}

これは、トリビアルにコピー可能なことをコンパイル時に判断して、std::memcpyなどの高速な方法に切り替えるメタプログラミングに置いて問題になる。そのため、コピー/ムーブコンストラクター、コピー/ムーブ代入演算子、デストラクターがdeletedかアクセス不可の場合、trivially copyableの条件を満たさないように、trivially copyableの定義を書き変える提案。

N4149: Categorically qualified classes

クラスが一時オブジェクトとなることを禁止できる機能と、永続的なストレージ上に確保されることを禁止できる機能の追加。


// 一時オブジェクトを禁止
struct X & { } ;
X f() ;

// 永続ストレージ上への確保を禁止
struct Y && { } ;
Y g() ;

int main()
{
    X x ; // OK
    f() ; // エラー
    X x = f() ; // OK、初期化子の中ではよい

    Y y ; // エラー
    g() ; // OK
}

一時オブジェクト禁止はどう使うかというと、std::asyncのように関数の戻り値を無視してほしくない場合や、scoped_guardのようなRAIIラッパーで、そもそも一時オブジェクトとして使うことを想定していないクラスに利用できる。

永続ストレージ上への確保の禁止はどう使うかというと、Expression Templateのplaceholder型のような、直接、名前付き変数として使うことを想定していないクラスに利用できる。これはN4035で提案されているものと同等の機能も実現する提案である。

両方とも、文脈上暗黙に型変換が起こる場合は、この制約から逃れることが出来る。

&と&&を両方指定すると、どちらも禁止するクラスを書くことができる。この場合、クラスは初期化の中で他の型に変換される場合でしか使うことができない。

struct X & &&
{
    operator int () { return 0 ; }
} ;

int x = X() ; // OK

また、std::as_temporary<T>というtype traitsが追加される。これはネストされた型名::typeが、Tがlvalue修飾子を持たぬ場合はT、持つ場合は変換関数の型、それ以外の場合はtypeは定義されないという仕様になる。

クラスのサブオブジェクトとして使用する場合には、この制約は適用されない。この理由について論文は、クラスの完全な型がオブジェクトがどう使われるかについて責任を持つべきだからとしている。

ドワンゴ広告

この記事はドワンゴ勤務中に書かれた。

ドワンゴは本物のC++プログラマーを募集しています。

採用情報|株式会社ドワンゴ

CC BY-ND 4.0: Creative Commons — Attribution-NoDerivatives 4.0 International — CC BY-ND 4.0

C++でfinally

一部の言語には、finallyという機能がある。あるブロックスコープを抜ける時に、必ず実行される処理を記述することが出来る。


try {
    // 処理
}
finally {
    // ブロック文を抜けた時に必ず実行される処理
}

C++にfinallyがない理由は、特に専用の文法が必要なく、ライブラリで十分なためだ。デストラクターとlambda式を使えばよい。

class scoped_guard
{
    std::function< void() > f ;
public :
    explicit scoped_guard( std::function< void () > f )
        : f(f) { }

    scoped_guard( scoped_guard const & ) = delete ;
    void operator = ( scoped_guard const & ) = delete ;


    ~scoped_guard()
    { f() ; }

} ;


int main()
{
    
    {
        scoped_guard guard( []{ /* ブロック文を抜けた時に必ず実行される処理 */ } ) ;
        // 処理
    }
}

ちなみに、これをもう少し汎用的に設計したものが、標準ライブラリに提案されている。

N3949: Scoped Resource - Generic RAII Wrapper for the Standard Library

ドワンゴ広告

この記事はドワンゴ勤務中に書かれた。

ドワンゴは本物のC++プログラマーを募集しています。

採用情報|株式会社ドワンゴ

CC BY-ND 4.0: Creative Commons — Attribution-NoDerivatives 4.0 International — CC BY-ND 4.0

2014-10-20

2014-10-pre-Urbanaのレビュー: N4138-N4141

C++の最新のドラフトとC++14 ISとなる予定でレビューに回される文書。

残念ながら、一般には公開されていない。

N4138: Editor's Report -- Working Draft, Standard for Programming Language C++

C++ドラフトの編集者による報告書

今回の変更は軽い文面上の修正にとどまる。

N4139: Editor's Report -- Programming Languages -- C++

C++14 ISの編集者による報告書。内容はN4138と同一

N4140: Working Draft, Standard for Programming Language C++

C++ドラフト

N4141: http://www.open-std.org/jtc1/sc22/wg21/prot/14882fdis/n4141.pdf

ドワンゴ広告

この記事はドワンゴ勤務中に書かれた。

ドワンゴは本物のC++プログラマーを募集しています。

採用情報|株式会社ドワンゴ

CC BY-ND 4.0: Creative Commons — Attribution-NoDerivatives 4.0 International — CC BY-ND 4.0

2014-10-pre-Urbana mailingsのレビュー : N4130-N4137

引き続き、2014-10-pre-Urbana mailingsを解説していく。

[Thou shalt not use PDF] N4130: Pad Thy Atomics

「汝のアトミックを詰めよ」と題された論文。

現在、atomicのデフォルトコンストラクターは、たとえオブジェクトが非トリビアルなコンストラクターを持っていようとも、オブジェクトの型としては未初期化の状態にすると規定されている。これはC言語との互換性のためだが、C++のクラス型も、未初期化の状態になってしまう。

これをCの互換性を保ったまま解決しようとすると、atomic型だけPODなのにPODではないとか、あるいは逆にPODではないのにPODであるなどとという例外的な状態にしなければならない。

未初期化で何が問題になるかといえば、padding bitsだ。未初期化の状態では、padding bitの状態が未規定である。では、たとえばatomic_compare_exchangeはpadding bitに対してどう振る舞うのか。結局、多くの実装ではatomicにはハードウェア支援されたロックフリーな機能を使うのであるからして、padding bitsも比較の際に考慮される実装がほとんどだろう。しかし、規格上はその挙動を定義していない。さてどうするか。

論文ではpadding bitsの状態が未規定であることによる弊害を列挙し、また問題を解決する提案をいくつか挙げているが、結論は出していない。

N4131: Another response to N4074; explicit should never be implicit

N4074で、return {expr}は、暗黙にexplicitコンストラクターを呼べるようにしようと提案されたが、それに対して、explicitは暗黙的になるべきではないとする反論。

N4074の提案では、以下のコードが合法になる。

struct Type1 {
    explicit Type1 (int);
};

Type1 example_f1 () {
    return { 0 };
}

曰く、explicitの意味は、「そういう初期化はできるぜ。でもたぶんお前の思っている結果にはならないぜ。マジでやりたいなら手を動かせ」という意味である。もし、利用者が本当にその初期化を望むのならば、明示的に書かなければならない。

曰く、そもそも現在のリスト初期化は、初期化の文法の統一を目的としたものであって、初期化の明示的なルールの迂回方法を目的としたものではない。

曰く、リスト初期化にはNarrowing Conversionがあるのに、なぜ制限を緩くするのだ。

曰く、return文は、そのオペランドであるexpression-or-braced-init-listを関数の戻り値として返すのではない。関数の戻り値の型の初期化子として使うのだ。提案では、関数の戻り値の型と関数のreturn文は、関数を書いた人間や保守する人間が把握しているとのことだが、そんなことはない。そもそも、戻り値の型を変えることはありえる。例えばstd::chrono::microsecondsを返すべきところでunsigned longを返していた関数を直すとかだ。そこで暗黙の型変換が走ってもらっては困る。

筆者も制限緩和には反対の立場である。

N4132: Contiguous Iterators

contiguous iteratorの提案。これは、ランダムアクセスイテレーターかつ、イテレーターが連続したストレージ上を指し示すもので、すなわち、イテレーターaと整数nにおいて、*(a + n)と*(std::addressof(*a) + n)が等しいイテレーターとなる。

原稿の標準ライブラリの中でcontiguou iteratorの制約を満たすものは、vector, string, valarray, arrayである。また、ライブラリTSに提案されているstring_viewもこの制約を満たす。

N4133: Cleanup for exception-specification and throw-expression

例外指定とthrow式の文面を整理する提案。意味上の変更はない。

[惜しむらくはPDF] N4134: Resumable Functions v.2

Resuamble Functionの提案。かなり長く、基本的な概念から解説していて、論文としてはよく書けている。前提知識の学習にも最適だ。この論文で提案しているのは、低級で軽量なスタックレスコルーチンだ。

論文によれば、この提案をMSVCで実装して知見を得たそうだ。論文著者はMS社員である。

ちなみにこの論文、9ページ目で、decltype(auto)と書くべきところを、declspec(auto)と書き間違えている。いくら著者がMS社員とはいえ、なかなかおもしろい間違い方をするものだ。

ただし、この提案はコア言語だけで完結せず、だいぶライブラリに依存する形になっているのだが、肝心のお手本となるべき標準ライブラリは含まれていない。論文には、コア言語が完成した後に、標準ライブラリとして我々が選ぶべきものが世上に開発されるであろう、という極めて楽観的な記述がある。また、全体的にやや理解が難しい。

不完全で難しい機能が使われるわけがなく、使われないものにライブラリが書かれるわけがない。これなら最初から純粋にライブラリだけで完結させたほうがマシだ。机上の上では悪くなさそうに見えるが、実際には使いにくい、いかにもMicrosoftらしき提案よ。

N4135: Language Support for Runtime Contract Validation (Revision 8)

N4075までは防衛的プログラミングと題されていた、実行時の前提条件検証ライブラリ。いわゆるassertライブラリの提案。

素晴らしい変更点として、プリプロセッサーの使用を推奨する文面が削られた。まだプリプロセッサーを使うところはあるのだが、コンパイル時のビルドモード切り替えにとどまっている。

このライブラリは、実行時の制約をチェックするためのassertライブラリである。従来のasertライブラリを模した作りにしているが、現実によくあるリリースビルドやデバッグビルドといったビルド時の切り替え機能に対応している。また、assertに引っかかった際に呼び出されるハンドラーを指定できる。

ビルドモード

opt, dbg, safeと、三種類のビルドモードと、何も指定しないという状態を合わせて、4種類のビルドモードが存在する。何も指定しない場合、一切のチェックが行われない。optは、最も軽い制約チェックのみ行う。dbgは、重いチェックも行う。safeは、すべてのチェックを行う。

ビルドモードは、ライブラリのヘッダーである<experimental/contract_assert>を#includeするまえに、プリプロセッサーマクロ、contract_assert_build_xxxを定義して指定する。

#define contract_assert_build_dbg 1
#include <experimental/contract_assert>

assertは、contract_assert_xxxで行う。例えばポインターの指し示す先のストレージをチェックしたいとする。


void do_something( char * p, std::size_t size )
{
    // 軽いチェック
    // nullポインターかどうかのチェックを行う
    contract_assert_opt( p != nullptr ) ;

    // デバッグ用のチェック
    // 独自に実装しているヒープメモリ上で確かに確保されているメモリかどうかを確認する
    contract_assert_dbg( custom_allocator::is_valid_area( p, size ) ) ;

    // 安全のためのチェック
    // ポインターの参照先のビット列が期待しているフォーマットを満たしているかどうかのチェックを行う
    contract_assert_safe( is_valid_format( p, size ) ) ;
}

なにもモードがつかないcontract_assertというものもあり、これはcontract_assert_dbgの別名となっている。

assertが失敗した時には、あらかじめ設定しておいたハンドラーが呼ばれる。ハンドラーはstd::contract_violation_info const &型の引数を取る。この型は、制約違反が起きた式の文字列、ファイル名、行番号を取得できる。例えば、以下は例外を投げるハンドラーである。

void violate()
{
    contract_assert( false ) ;
}

int main()
{
    std::set_contract_violation_handler(
        []( auto && info )
        { throw info ; } ) ;

    try {
        violate() ;
    } catch( std::contract_violation_info const & info )
    {
        std::cout
            << "expression: " << info.expression_text << '\n'
            << "filename: " << info.filename << '\n'
            << "lien number: " << info.line_number << '\n' ;
    }
}

また、スレッドごと、ブロックスコープごとのハンドラーを設定するには、std::contract_violation_guardというライブラリが用意されている。

void f()
{
    {
        std::contract_violation_guard g( [](auto && info ){ throw info ; } ) ;
        // gのハンドラーが有効
    }

    // gのハンドラーは無効
}

また、ユニットテスト用に、assertの可否によってbool値を返す機能もある。

void do_something( int * ptr )
{
    contract_assert( ptr != null ) ;
}

int main()
{
    int i = 0 ;
    bool b1 = contract_assert_pass( do_something( &i ) ) ; // true
    bool b2 = contract_assert_pass( do_something( false ) ) ; // false

    bool b3 = contract_assert_fail( do_something( &i ) ) ; // false
    bool b4 = contract_assert_fail( do_something( nullptr ) ) ; // true
}

[読む気の失せる典型的な学術論文クソ固定レイアウトのPDF] N4136: C Concurrency Challenges Draft

これは読む気が失せるほどにクソ典型的クソ学術クソ論文クソ固定クソレイアウトのクソPDFにて候。こののケンブリッジ大学の著者5人に申すことの候。彼らは天然の石頭にして、要望はすなわち鶏卵を岩石に投ずると一般なるも、あえて言わん。汝ら論文を紙で読みたるや。否、たとい紙で読みつるとも、物理的な単一ページを論理的な複数ページに分割するのは、そも何のためぞや。美しとでも思うてありや。うるさきことこの上なし、やめよ。

中身は、本来学術論文としてだす予定の論文の下書き原稿にて候。C++標準化委員会の文書としてもいだしたとなん言いける。

肝心の内容は、CとC++のアトミック周りの定義と理論の解説と、所謂thin air(虚空から現れいでたる)と名づけたるプログラムからは何らの脈絡もなき挙動の解決の難しきを論じ、結論して曰く、解決策なしと。

N4137: Business Plan and Convener's Report

議長のHerb SutterによるISOに提出するお堅い報告書

ドワンゴ広告

この記事はドワンゴ勤務中に書かれた。

ドワンゴは本物のC++プログラマーを募集しています。

採用情報|株式会社ドワンゴ

CC BY-ND 4.0: Creative Commons — Attribution-NoDerivatives 4.0 International — CC BY-ND 4.0

2014-10-18

XNGという新しいアニメーション画像フォーマット

[Phoronix] GNOME Developer Comes Up With New Animated Image Format

XNG: GIFs, but better, and also magical | Clean Rinse

GNOME開発者のJasper St. Pierreが、GIFに変わる新しいアニメーションフォーマットを作り出したそうだ。

ただ・・・作ったというか、ハックしたというか。

この新しいフォーマットは、なんとブラウザーのvideoやcanvas要素を使うものではなく、もちろんJavaScriptのライブラリを使う必要もない、単にimg要素のsrc属性に指定するだけで既存のブラウザー上で動作する。<img src="foobar.xng">「魔法だ」

原理としては、ファイルの中身はSVGファイルで、BASE64エンコードされたJPEG画像が入っている。

まあ、面白いといえば面白いが、普通にvideo要素を使ったほうがいいのではないかと思う。

2014-10-15

2014-10-pre-Urbana mailingsのレビュー: N4122-N4129

2014-10-pre-Urbana mailingsが公開された。

N4122: AGENDA, PL22.16 Meeting No. 64, WG21 Meeting No. 59, November 3-8, 2014 – Urbana-Champaign, IL

2014年11月3-8日にUrbana-Champaignで開かれる会議の予定表。

N4123: Improvements to the Concurrency Technical Specification

Concurrency TSドラフトの変更提案。すでに提案されていたexperimentalヘッダーなどの細かい変更に対応するため。

N4124: Toward More Expressive Iterator Tags

イテレーターカテゴリーを表現するライブラリであるイテレータータグの定義を変更する提案。

イテレーターカテゴリーの実際の内容を個別に、reference_tagとかlvalue_tagなどと細分化し、イテレータータグはこれらの個別のタグの組み合わせで表現する。

現在のイテレータータグは、以下のように定義されている。

struct input_iterator_tag { };
struct output_iterator_tag { };
struct forward_iterator_tag: public input_iterator_tag { };
struct bidirectional_iterator_tag: public forward_iterator_tag { };
struct random_access_iterator_tag: public bidirectional_iterator_tag { };

タグというのは、異なる型として認識さえされればいいので、この定義なのだが、これではややわかりにくい。さらに、最近提案されているイテレーターは、ランダムアクセスイテレーターにみえて、実は既存のランダムアクセスイテレーターの定義をすべて満たしていないという問題が指摘されている。

このため、イテレーターができることを細分化したタグを定義し、既存のイテレータータグを、そのタグの組み合わせとして定義するという提案。

これを使い、既存のイテレータータグを、以下のように、組み合わせて定義する。

typedef packer<reference_tag,
               lvalue_tag,
               rvalue_tag,
               equality_comparable_tag,
               multipass_tag>
          forward_iterator_tag;

前回のN4068からの変更は、タグを組み合わせるために、N4115で提案されている汎用のコンパイル時限定tupleもどきであるpackerをつかうことになった。

これにより、例えば、あるイテレータータグの特性をコンパイル時に判定できるようになった。

template < typename Iterator >
void f( Iterator first, Iterator last )
{
    // Iteratorがプロクシーイテレーターかどうか判定
    constexpr bool b =
        std::contains_type_v<
            std::iterator_traits<Iterator>::iterator_category,
            std::packer< std::reference_tag > > ;
}

[PDF注意] N4125: 2014-09 WG21/SG1 Meeting Information

なぜか素手の終わった2014年夏の標準化委員会の会議の会場案内。CppConの前の週に行うそうで、CppConの会場も近い。

N4126: Explicitly defaulted comparison operators

デフォルトのメンバーごとの比較演算子を明示的に自動生成させようという提案。

クラスの比較が、クラスのメンバーごとの比較に落とし込め、クラスのメンバーは比較演算子をサポートしているとき、以下のようなコードを書かなければならない。

struct X
{
    int a, b, c ;

    bool operator ==( X & rhs ) const noexcept
    {
        return
            a == rhs.a &&
            b == rhs.b &&
            c == rhs.c ;
    }

    bool operator != ( X & rhs ) const noexcept
    {
        return !( *this == rhs ) ;
    }

    bool operator < ( X & rhs ) const noexcept
    {
        return std::tie( a, b, c ) < std::tie( rhs.a, rhs.b, rhs.c ) ;
    }

    bool operator > ( X & rhs ) const noexcept
    {
        return rhs < *this ;
    }

    bool operator <= ( X & rhs ) const noexcept
    {
        return !( rhs < *this ) ;
    }

    bool operator >= ( X & rhs ) const noexcept
    {
        return !( *this < rhs ) ;
    }

} ;

これは極めて面倒だ。これを間違えずに書くのは至難の業である。しかもこれはstd::tieを使っている。本当のメンバーごとのlexicographical comparisonは正しく書くのがひたすら苦行でめんどくさい上に、手で書くと間違えやすい。

そして、このコードは、もしメンバーの追加や削除が行われた場合、手で書き換えなければならない。極めて間違いやすい非人間的なコードであり、このコードを手で書くのは深刻な人権侵害である。こういうコードはよろしくコンパイラーが自動生成すべきなのだ。

互換性の問題もあるので、暗黙に比較演算子を生成させるわけには行かない。そこで、明示的な宣言によって生成させる文法を提案している。

struct Thing
{
    int a, b, c;
    std::string d;
};

// 明示的な自動生成
bool operator==(const Thing &, const Thing &)= default;
bool operator!=(const Thing &, const Thing &)= default;

メンバーがprivateな場合、演算子はfrined宣言しなkればならない。

class AnotherThing
{
    int a, b;

public:
    // ...

    friend bool operator<(Thing, Thing) = default;
    friend bool operator>(Thing, Thing) = default;
    friend bool operator<=(Thing, Thing) = default;
    friend bool operator>=(Thing, Thing) = default;
};

なるほど、確かに中身を書く必要はなくなったが、まだ冗長で面倒だ。そこで、もっと簡潔な文法も提案されている。

struct Thing
{
    int a, b, c;
    std::string d;

    default: ==, !=, <, >, <=, >=;   // defines the six non-member functions
};

前回の論文N3950からの変更点としては、論文で議論が必要だとされていた部分が会議によってコンセンサスが得られたところだ。ポインターや浮動小数点数やenum型はそのまま比較される。mutableメンバーが存在する場合は明示的な演算子が使用できない。簡潔な文法は会議で強力に支持された。

論文では、大小比較演算子の実装方法を二つ示している。ひとつはメンバーのstrict weak orderを使うもの、つまり大小比較演算子のみ使う。


bool operator<(const thing &t1, const thing &t2) 
{
    if (t1.a < t2.a) return true;
    if (t2.a < t1.a) return false;
    if (t1.b < t2.b) return true;
    if (t2.b < t1.b) return false;

    return t1.c < t2.c;
}

これは既存のpairやtupleも採用している比較方法である。

もうひとつは、メンバーのtotal orderを利用するもので、これは等号演算子の存在が必要になる。

bool operator<(const thing &t1, const thing &t2) 
{
    if (t1.a != t2.a)
        return t1.a < t2.a;
    if (t1.b != t2.b)
        return t1.b < t2.b;
    
    return t1.c < t2.c;
}

これは是非とも採用されて欲しい。

N4127: Checked-dereference conditions


if ( T x : e ) s

if ( auto && __p = e )
{
    T x = *__p ;
    s
}

に変換する文法の提案。

ポインターや、ポインター風の型の値をbool型に変換してfalseではないことを確かめてから、operator *()でデリファレンスして使うというコードは頻出する。そのようなコードを書くための短い文法だ。Range-based forの文法から着想を得ているらしい。

具体的な利用方法としては、例えば


void inc( int * ptr )
{
    if ( int & ref : ptr )
        ++ref ;
}

とか、

void inc( std::weak_ptr<int> wp )
{
    if ( T & ref = wp.lock() )
        ++ref ;
}

などのように使える。他には、提案中のoptionalのようなライブラリも利用例も考えられる。

この文法は、forやwhileでも使える。switchでは使えない。

また、「次世代」版として、型を省いてデフォルトでauot &&にする文法も提案されている。つまり、


if ( x : e ) s

if ( auto && __p = e )
{
    auto && x = *__p ;
    s
}

になる。

N4128: Ranges for the Standard Library, Revision 1

標準ライブラリにRangeの概念を導入した場合の設計の叩き台。

様々な設計上の考察が書かれている。

[PDF大注意] N4129: Source-Code Information Capture

ソースファイルの記述場所の行番号、行頭からの文字数、ファイル先頭からの文字数、ファイル名、関数名を取得するライブラリの提案。

source_context型のオブジェクトを構築した場所の情報が取得できる。

void f()
{
    std::source_context sc ; // この場所のソースファイル情報

    std::cout
        << "file name: " << sc.file_name() << "\n" <<
        << "line number" << sc.line_number() << "\n" ;

}

プリプロセッサーにたよらずに、ソースファイルの情報を取得できるようになる。いわゆるprintfデバッグ用やログの出力などに利用できる。

ドワンゴ広告

この記事はドワンゴ勤務中に書かれた。

ドワンゴでは、デリランチと称する新しい重量課金の昼食システムにより、昼職が劇的に改善されている。サラダが安い。

ドワンゴは本物のC++プログラマーを募集しています。

採用情報|株式会社ドワンゴ

CC BY-ND 4.0: Creative Commons — Attribution-NoDerivatives 4.0 International — CC BY-ND 4.0

2014-10-14

私宛に届いた料理本

私宛に注文した覚えのないアマゾンの小包が届いた。不思議に思って開けてみると、が入っていた。 Indoor! Grillingという本が入っていた。

読んでみたが、いかにもステレオタイプなアメリカ人が好みそうな大雑把な味付けで、火さえ通せばとりあえず食えそうな料理が載っていた。

パラパラとめくってみたところ、ほとんどのレシピが、わざわざ作り方を解説するまでもない焼くだけの料理のようだ。

さて、どうしようか。いずれ作ってみるかもしれない。

2014-10-12

GNU/Linuxでコマンドラインから喋らせる方法

本の虫: Pingプログラムの話で、pingが通るたびに合成音声ソフトウェアにpingと喋らせた運用が紹介されている。実際にやってみたくなったので、方法を調べることにした。

まず、音声合成を行うコマンドラインのGNU/Linux用のソフトウェアを探さねばならない。調べると、espeakというソフトウェアが見つかった。

apt-get install espeak
espeak "Hello. I am espeak."

espeakは、何も引数を指定しない場合、stdinから読み取って音声合成していくれる。完璧だ。

さっそく、ためしてみた。

ping localhost | sed -e "s/.*/ping/g" | espeak

なぜか動かない。これはsedの出力がバッファされているためだった。

プログラムのバッファリングの外部から操作するには、stdbufが使える。これはGNUのcore utilsに入っているので、ほぼすべてのGNU/Linuxディストロには標準で入っているだろう。

ping localhost | stdbuf -oL sed -e "s/.*/ping/g" | espeak

と書いた後で、sedにもバッファリングを無効にするオプションがあることに気がついた。

ping localhost | sed -u -e "s/.*/ping/g" | espeak

読書

昨日、妖怪ハウスで住人の誕生日会があった。今回は予想よりは人が少なかったが、それなりに騒がしく疲れた。三連休の残りは、読書でもしながらのんびりと過ごすことにする。

読書といえば、カンタベリー物語を読んだ。カンタベリー物語は、様々な身分の者が連れ立って巡礼に向かう途中、それぞれ帰りの宿の飯のおごりをかけて話を語り合うという設定の説話集だ。作者であるチョーサー自身が登場している。

話は、二人の騎士が共通の女に惚れたため決闘して取り合う話から始まったが、その後に続く話の大半は、好色な話ばかりであった。

さて、今はカンタベリー物語の翻訳本に一緒に載っている、ガルガンチュア物語を読んでいるが、あまりに大げさな表現が多いため、面白いもののやや飽き始めている。

Pingプログラムの話

The Story of the PING Program

そうだ。UNIXのpingを書いたのは俺さ。pingというものは誰でも知ってるだろうが、これはある夜、俺が千行程度で書いたハックだ。

名前の由来はソナーの音だ。俺は大学でソナーとレーダーシステムの設計をさんざんやっていたので、サイバースペース的なものとは親和性が高かった。新しい分野に既存の概念を適用したというわけだ。pingはIP/ICMP ECHO_REQUESTとECHO_REPLYパケットを使って時間を測り、ターゲットマシンへの「距離」を計測する。

4.2a BSD UNIX用のPINGを書いた着想は、1983年の7月に、ノルウェイでのDARPA会議で、Dave MIlls博士からきいた話が元だ。博士はFizzball LSI-11システムで、ICMP Echoパケットの時間を計測して、経路遅延を計測したと話していた。

1983年の12月、俺はBRLのIPネットワークで奇妙な挙動に出くわした。Mills博士の話を思い出して、俺はすぐにPINGプログラムを書いた。生のICMPのためにSOCK_RAW_AF_INETでバークレー流のソケットを開くものだ。コードは問題なくコンパイルできたが、動かなかった。カーネルが生ICMPソケットをサポートしていなかったのだ。ムカつきながら、カーネルを書いて、夜明け前に全部動くようにした。まあ、Chuck Kennedy(Kermit)が問題があることを把握して、俺が最初の"ping"パケットを発射する前にネットワーク機器を直してしまった。とはいえ、それ以降、何回か使っていた。やれやれ、もし俺が当時、これが俺の人生における最大の最も有名な実績であると知っていたならば、もう数日は使ってもっとオプションを加えていたものを。

バークレーの連中は喜び勇んで俺のカーネルの変更と、PINGソースコードを持ち帰って、それからそれがバークレーUNIXの標準の一部になった。自由なので、Microsoft 95やWindows NTを含む多くのシステムに移植された。表示するメッセージによって判別できる。このようになる。

PING vapor.arl.army.mil (128.63.240.80): 56 data bytes
64 bytes from 128.63.240.80: icmp_seq=0 time=16 ms
64 bytes from 128.63.240.80: icmp_seq=1 time=9 ms
64 bytes from 128.63.240.80: icmp_seq=2 time=9 ms
64 bytes from 128.63.240.80: icmp_seq=3 time=8 ms
64 bytes from 128.63.240.80: icmp_seq=4 time=8 ms
^C
----vapor.arl.army.mil PING Statistics----
5 packets transmitted, 5 packets received, 0% packet loss
round-trip (ms)  min/avg/max = 8/10/16

1993年、PINGを書いてから10年後のこと、USENIX団体は俺にご立派な賞状をよこして、俺をUSENIX団体1993粘土生涯貢献章にした。バークレーはカリフォルニア大学、コンピューターシステムズリサーチグループ(CSRG)での話だ。「優秀なる知的な達成と業界への類なき貢献に経緯を評してこれを送る。CSRGの理念に基づき、以下の個人とその団体をCSRGへの貢献者と認める」、おやおや。

ソースコード(40K)を読みたいかい?

俺の考えでは、PINGはPacket InterNet Grouperの略ではなく、ソナー用語である。でもまあ、dave Millsはこの名称を言っていたと人からきいたので、まあ、療法とも正しいのかもしれん。やれやれ、行政機関ってのは略語のセンスがないな。

Phil DykstraはPINGをICMPレコードルートに対応させたが、この黎明期には、対応しているルーターが少なかったため、この機能はほぼ約立たずとなっていた。IPヘッダーが記録できるhop数の制限により、長い経路を計測する妨げになっていた。

LBLのVan Jacobsonが、IP time to life(TTL)フィールドを変更していくことにより、ICMP Time-to-Live Exceededメッセージを受け取れることに気が付き、俺のカーネルのICMPサポートを利用して、TRACERROUTEを書いたのは猛烈に嫉妬した。俺が考えつければよかったのに! もちろん、ルーターはICMPメッセージに対するICMPエラーメッセージを実装していないので、現実のtracerouteはUDPデータグラムを使うのだが。

俺がきいた最高のping活用の逸話は、USENIXカンファレンスできいたもので、不定期に不調になるEthernetを抱えていたネットワーク管理者が、pingプログラムを音声合成プログラムにつないで、つまりは以下のように書いて、

ping goodhost | sed -e 's/.*/ping/' | vocoder

音声合成プログラムの出力を事務所のステレオにつないで、音量を最大にした。コンピューターが1秒おきに"Ping, Ping, Ping..."と怒鳴り続けている間に、管理者は音が止まるまで建物内のEthernetコネクターをいじくりまわしていた。そして故障箇所を特定したのだ。

ちなみに、pingプログラムの作者、Mike Muussは、交通事故により2000年11月20日に亡くなっている。

2014-10-09

C++の例外指定

C++では、関数に例外指定というものが記述できる。これはC++98からある機能で、throw(T1, T2, ...)という文法で、関数が外に投げる例外を指定する機能だ。

// C++98/03
void f() throw( int, double ) ;

もし、関数が例外指定に指定された以外の型を投げた場合、std::unexpectedが呼ばれる。

void f() throw( int ) 
{
    throw 0 ; // OK
    throw 0.0 ; // 実行時エラー: call std::unexpected() 
}

例外指定は、関数から抜けだせる例外を指定する機能で、関数の外に例外を投げなければ問題がない。

void f() throw( int ) 
{
    try {
        throw 0.0 ; // OK
    } catch( ... ) { } // OK、外に投げない
}

というのは、C++11以前の歴史の話だ。

現実に、この例外指定を真面目に実装するコンパイラーは少なかった。パースだけして無視するような有名なコンパイラーがはびこったのだ。結局、実装されないのであれば机上の空論でしかない。C++11では、これを動的例外指定と呼び、deprecated扱いしている。規格が現実に追いついたというわけだ。

ところで、この例外指定の文法は、本来規格が想定していなかった意味で使われ始めた。動的例外指定は、文法上、型を指定しないこともできる。

void f() throw( ) ;

ある主要なC++コンパイラーは、この記述を、関数は例外を外に投げないという意味にみなし、最適化のためのヒントに使い始めた。これは、本来規格設計時に意図された意味ではなかったが、最適化のヒントのため、広まった。世の中のC++コンパイラーは、動的例外指定を完全に実装しなかったが、これは実装するだけの価値があったのだ。

C++11では、無例外保証の指定機能だけに特化した、noexceptが追加された。noexceptを指定すると、関数は外に例外を投げないと指定したことになる。

void f() noexcept ;

もし外に例外を投げると、std::terminateが呼ばれる。

また、noexceptにはbool型のコンパイル時定数のオペランドを取る文法がある。式がtrueの時は無例外指定になり、falseの時は無例外指定にならないという意味を持つ。

void f() noexcept(true) ; // 例外を外に投げない
void g() noexcept(false) ; // 例外を外に投げる可能性がある

これにより、コンパイル時メタプログラミングにより、関数の無例外指定の有無を切り替えられるようになった。

無例外指定のない通常の関数宣言は、例外を外に投げる可能性がある関数である。throw()も、無例外指定とみなされる。

void f1() ;
void f2() throw() ; // 無例外指定
void f3() noexcept ; // 無例外指定
void f4() noexcept( true ) ; // 無例外指定
void f5() noexcept( false ) ;

また、C++11ではnoexcept式というものが追加されている。これは、オペランドの式が例外を投げるかどうかをboolで返す。オペランドの式は未評価式である。

void f() ;
void g() noexcept ;

int main()
{
    noexcept( f() ) ; // false
    noexcept( g() ) ; // true
}

ドワンゴ広告

この記事はドワンゴ勤務中に書いた。

ドワンゴは本物のC++プログラマーを募集しています。

採用情報|株式会社ドワンゴ

CC BY-ND 4.0: Creative Commons — Attribution-NoDerivatives 4.0 International — CC BY-ND 4.0

OpenBSD、headに37年前から存在したBill Joyのバグの修正に22年前のKeith Bosticの変更を適用

src/usr.bin/head/head.c - view - 1.18

Bill Joyが1977年8月24日にもたらし、1978年3月9日の1BSDのリリースに存在していた37年前のバグを、Keith Bosticの22年前の修正を4.4BSDからマージ(マジだぜ)

当時のCSRG SCCSコミットメッセージ

^As 00009/00006/00145
^Ad D 5.7 92/03/04 14:35:42 bostic 9 8
^Ac can't use freopen; example is "date | head file1 /dev/stdin"

Bill Joyのコードは、読み込むべきファイルを、stdinをストリームとしてfreopenすることにより、getcharで指定のファイルを読むこむものだが、修正後は、真面目に新しいファイルハンドルを得て、getcで読み込んでいる。

なんだか大昔のおおらかなコードの書き方を見た気がする。

ドワンゴ広告

この記事はドワンゴ勤務中に書いた。

なぜか社内で例外指定について聞かれたので、後で歴史をまとめておく。

ドワンゴは本物のC++プログラマーを募集しています。

採用情報|株式会社ドワンゴ

CC BY-ND 4.0: Creative Commons — Attribution-NoDerivatives 4.0 International — CC BY-ND 4.0

C++標準化委員会の文書: 1989年

Index of /jtc1/sc22/wg21/docs/papers/1989

暇なので、C++標準化委員会であるCS22/WG21の、歴史的な文書を読んでみようと思い立った。現在入手できる最古のドキュメントは、1989年のものだ。

Transmittal of X3 Lette Ballot 1269, Approval of X3/89-738R, Project Proposal for C++, and assignment to a new technical commitee, X3J16

1989年7月11日から13日にかけての会議で、SPARCとLenkov氏は、X3/89-738、すなわちC++と新しい技術委員会の設立に対する提案、を検討し、いくつかの文面上の提案を行った。SPARCは、C++はプログラミング言語Cとは区別されるべき別言語であるという一般の理解を認め、X3J11はこのプロジェクトが別の技術委員会に属するものであることを認めた。SPARCは新しい技術委員会に対する支援が存在することを認め、また、Lenkov氏はプロジェクトの提案が承認された場合、団体会議の議長を務めることに同意した。

ドキュメントの日付は1989年8月23日。これを承認している団体は、アメリカ合衆国におけるANSIとISOの橋渡しをする団体、日本で言えばITSCJにあたるINCITS、当時はCBEMAとかX3などと呼ばれていた公団らしい。

Lenkovとは、ヒューレットパッカードのカリフォルニア言語研究所のDmittry Lenkovのようだ。

X3_89-738R Programming Language C++ Proposal.pdf

1989年8月27日に提出された、Dmittry Lenkovによるプログラミング言語C++の技術委員会を設立する提案書。

C++の標準化のための技術委員会の設立を提案し、早急に対応できない場合は、方言を乱立、言語機能の欠如、ライブラリの欠如、対応環境の欠如、標準規格が存在しないがために新規プロジェクトに採用されないといった問題を生むと主張している。

今読んで興味深いのは、言語の欠如にGCを挙げている点だ。

X3J16 C++ Standard Committee.Organizational Meeting Nov_2_1989.pdf

1989年11月2日に行われた会議の議題一覧と見える。

12_08_1989 An attempt at defining ambiguity resolution for C++.pdf

T &にCV修飾子付きの型をマッチさせるということや、オーバーロード解決の詳細なルールなどが記述されている。今と違って、用語がだいぶ不統一である。

ドワンゴ広告

この記事はドワンゴ勤務中だが、次のC++論文集の発行まで暇なので、何かしているふりをしようとして書いた。

ドワンゴは本物のC++プログラマーを募集しています。

採用情報|株式会社ドワンゴ

CC BY-ND 4.0: Creative Commons — Attribution-NoDerivatives 4.0 International — CC BY-ND 4.0

2014-10-08

妖怪ハウスにシットアップベンチを導入した

妖怪ハウスには筋トレ中毒の両生類がいて、最近手足が生えてきたらしく調子に乗って筋トレをしているので、シットアップベンチを買い与えてみた。なかなかお気に入りらしい。次は回し車だろうか。

さて、シットアップベンチだが、私にはその価値がよくわからない。腹筋よりも、むしろ背筋を鍛えるのに便利そうだ。

そういえば、私の誕生日を記念して、10月25日に妖怪ハウスでボドゲ会をする予定だ。

本の虫: 妖怪ハウスでボドゲ会の告知(10/25)

C++のtypeidとtype_infoとtype_index

10月の論文集が発表されるまで暇なので、typeidとtype_infoと、C++11から追加されたtype_indexの解説をする。

C++でRTTI(Run Time Type Infomation/Identification)と呼ばれる機能がある。

std::type_infoは、型情報を表現するクラスである。type_infoのオブジェクトは、typeid式で得ることができる。typeid式は、オペランドに式かtype-idを取る。typeid式の結果は、constなstd::type_info型のlvalueである。typeid式を使うには、typeinfoヘッダーをincludeする必要がある。

#include <typeinfo>

std::type_info const & t1 = typeid(int) ;
std::type_info const & t2 = typeid(double) ;

std::type_infoはデフォルト構築もコピーもムーブもできないクラスである。std::type_info型のオブジェクトは、typeid式によってしか作れない。

typeid式は、constなtype_info型のlvalueを返すので、type_infoを使う場合は、リファレンスかポインターで参照しなければならない。


std::type_info const & t1 = typeid(int) ;
std::type_info const * t2 = &typeid(int) ;

typeid式が返すオブジェクトの寿命は、通常とは変わっていて、プログラムが終了するまでである。つまり、以下のようなコードが動く。

#include <typeinfo>

int main()
{
    std::typeinfo const * ptr ;
    {
        ptr = &typeid(int) ;
    }

    ptr->name() ; // OK、
}

typeidが返すオブジェクトの寿命はプログラムの終了までなので、リファレンスやポインターで安全に保持できる。寿命や解放を考える必要はない。

type_infoのオブジェクトは型を表現する。type_infoのオブジェクト同士を等号比較することで、型が等しいかどうかを調べられる。


typeid(int) == typeid(double) ; // false

また、nameというメンバー関数を持っていて、これは実装依存の型を表現する何らかのnull終端された文字列を返す。

int main()
{
    std::cout << typeid(int).name() << '\n' ;
}

GNU/Linuxでは、IntelのItanium用のC++ ABIがデファクトスタンダードとなっていて、GNU/Linux用のGCCやClangでは、この仕様に従ったマングル名が返される。デマングルして人間にわかりやすい型にするには、以下のようにすればよい。

#include <cxxabi.h>
#include <cstdlib>
#include <string>
#include <memory>

struct free_delete
{
    template < typename T >
    void operator ()( T * ptr ) const noexcept
    {
        std::free( ptr ) ;
    }
} ;

std::string demangle( std::type_info const & ti )
{
    int status = 0 ;
    std::unique_ptr<char, free_delete >
        ptr( abi::__cxa_demangle( ti.name(), nullptr, nullptr, &status ) ) ;

    if ( !ptr )
    {
        switch ( status )
        {
        case -1 :
            return "memory allocation failure" ;
        case -2 :
            return "invalid mangled name" ;
        case -3 :
            return "invalid arguments" ;
        default :
            return "Shouldn't reach here" ;
        }
    }

    std::string result( ptr.get() ) ;

    return result ; 
}

それはさておき・・・

typeid式が実行時型情報と呼ばれている理由は、ポリモーフィッククラス型の式をオペランドに与えると、実行時になるまで分からない型を表現するtype_infoが返されるからだ。

struct A
{
    virtual void polymorphic() { }
} ;

struct B : A
{ } ;

void f( A & ref )
{
    // refがA, Bどちらを参照するのかは実行時に決まる
    // tiが表現する型も実行時に決まる
    decltype(auto) ti = typeid( ref ) ;
}

これにより、以下のようなコードを書くことができる。

struct Base { virtual void p() = 0 ; } ;
struct Cat : Base { } ;
struct Dog : Base { } ;

std::string get_name( Base & ref )
{
    decltype(auto) ti = typeid(ref) ;
    if ( ti == typeid(Cat) )
        return u8"猫" ;
    else if ( ti == typeid(Dog) )
        return u8"犬" ;

    return u8"謎" ;
}

もちろん、世間一般的に、この場合にはvirtual関数を使うのが礼儀正しい作法であるとされている。

struct Base { virtual std::string get_name() = 0 ; } ;
struct Cat : Base { std::string get_name() { return u8"猫" ; } } ;
struct Dog : Base { std::string get_name() { return u8"犬" ; } } ;


auto get_name( Base & ref )
{
    return ref.get_name() ;
}

さて、typeidの結果であるtype_info型は、かなり扱いづらい。すでに説明したように、type_infoはデフォルト構築、コピー、ムーブができず、typeid式から得られるオブジェクトを、リファレンスやポインター経由で参照して使うしかないからだ。これは、type_info型のオブジェクトの大量に管理したい場合に問題になる。オブジェクトを大量に管理するには、vectorやmapやunordered_mapなどのコンテナーを使いたいが、type_info型を直接使うわけには行かない。とすると、ポインターだろうか。

std::vector< std::type_info * > v ;

vectorならこれでよくても、やはり型情報という性質上、それぞれの型に何らかの値を付属させて、後から検索したい都合も出てくる。mapやunordered_mapで使いたい。

そのためには、type_info *型をラップするという手がある。しかし、正しくラップするのは、以下のように単調でめんどくさい。

namespace ezoe {

class type_index
{
public:
    type_index(const std::type_info& rhs) noexcept
        : target( &rhs ) { }
    bool operator==(const type_index& rhs) const noexcept
    { return *target == *rhs.target ; }
    bool operator!=(const type_index& rhs) const noexcept
    { return *target != *rhs.target ; }
    bool operator< (const type_index& rhs) const noexcept
    { return target->before( *rhs.target ) ; }
    bool operator<= (const type_index& rhs) const noexcept
    { return !target->before( *rhs.target ) ; }
    bool operator> (const type_index& rhs) const noexcept
    { return rhs.target->before( *target ) ; }
    bool operator>= (const type_index& rhs) const noexcept
    { return !rhs.target->before( *target) ; }
    std::size_t hash_code() const noexcept
    { return target->hash_code() ; }
    const char* name() const noexcept
    { return target->name() ; }
private:
    const std::type_info* target;
} ;

}

namespace std
{
    template < >
    struct hash< ezoe::type_index >
    {
        size_t operator() ( ezoe::type_index ti ) const noexcept
        {
            return ti.hash_code() ;
        }
    } ;
}

このコードは、誰が書いてもこのようになる。しかし、これを正しく書くのは面倒で、間違いやすく、しかもお互いに非互換なラッパーが乱立してしまう。そこで、このようなラッパー、std::type_indexが、C++11では標準ライブラリに追加された。使うには、ヘッダーファイルtypeindexをincludeする必要がある。

#include <typeindex>
#include <map>

int main()
{
    std::map< std::type_index, std::string > m =
    {
        { typeid(int), "int" },
        { typeid(double), "double"}
    } ;

    std::cout << m[typeid(int)] << '\n' ;
}

ドワンゴ広告

この記事はドワンゴ勤務中に書かれた。

次に解説すべきC++11で追加されたライブラリを探している。

ドワンゴは本物のC++プログラマーを募集しています。

採用情報|株式会社ドワンゴ

CC BY-ND 4.0: Creative Commons — Attribution-NoDerivatives 4.0 International — CC BY-ND 4.0

2014-10-07

ゲーム用PCが欲しいが、今は時期が悪い

Borderlands 2がGNU/Linuxに移植された。

[Phoronix] Borderlands 2 Launches On Steam For Linux

そして、もうすぐ発売されるBorderlands Pre Sequelも、GNU/Linuxに対応している。

Pre-purchase Borderlands: The Pre-Sequel on Steam

このニュースを見て、にわかにゲーム熱が沸き上がってきた。Borderlandsは不自由なソフトウェアであるが、Windowsを使わなくても実行できるのであれば、興味深い。しかし、ゲームをするためには、ゲーム用のPCが新たに必要だ。

パーツの価格を調べたり、各ショップのBTOをみてみた結果、私の満足できるスペックには、20万円ほどかかることが判明した。

しかし、ふと考えてみれば、現在のところ遊ぶ予定のゲームは、Borderlands 2とPre Sequelだけだ。どうやらGNU/Linuxでも32bitプロセスであり、あまりに大量のメモリを積んでもディスクキャッシュにしかならない。

それを踏まえて妥協すると、十数万円と言ったところだ。

しかし、いまゲーム用PCを用意するには、やや面倒な事情がある。GPUだ。

いまゲーム用GPUを選ぶのであれば、値段と性能と電力効率から考えて、GeForce GTX 900シリーズが最も良いように思われる。

問題は、GTX900シリーズは先月市場に出たばかりで、nVidiaのバイナリブロブドライバーのバージョン343.22を必要とする。

主要なGNU/Linuxのディストリビューションは、まだこの最新のnVidiaの不自由なドライバーをパッケージ化していない。nVidiaのGNU/Linux用の不自由なバイナリブロブドライバーの手動インストールは、あまり自力でやりたい種類の作業ではない。あるいは、xorg-edgersのようなPPAを使うという手もあるが。

というわけで、今はまだ時期が悪い。しばらくはカンタベリー物語でも読むことにする。

ドワンゴ広告

この記事はドワンゴ社内で書いた。

なぜこんなことを書いているかというと、次のC++標準の論文集が出るのは、10月半ばなので、すこし暇なのだ。最近C++14の新機能を解説し続けているのもそのためだ。あるいは、この機会に標準ライブラリの解説でもするべきだろうか。

ドワンゴでは、社内にゲーム専用機が置かれていて、たまに社員が遊んでいる。対戦できる格ゲーが多いようだ。中でも興味深いのは、メガドライブが置いてあることだ。何でも、ぷよぷよのために置いてあるらしい。最高のぷよぷよはメガドライブのものであるという話だが、筆者は真偽を判定できるほどぷよぷよをやり込んではいない。

ドワンゴは本物のC++プログラマーを募集しています。

採用情報|株式会社ドワンゴ

CC BY-ND 4.0: Creative Commons — Attribution-NoDerivatives 4.0 International — CC BY-ND 4.0

2014-10-06

C++14の新機能: サイズ付き解放関数

ストレージのサイズを引数で受け取れるグローバルな解放関数のオーバーロードを追加する提案。

これまで、解放関数は、解法すべきストレージのサイズを知ることができなかった。

void operator delete ( void * ptr ) noexcept
{
    // ptrが指し示すストレージのサイズは分からない
}

これは、メモリ管理に、K&Rにも書いてあるような古典的な方法を用いているのならば特に問題にはならない。ストレージのサイズまで管理しているからだ。しかし、メモリ確保のアルゴリズムは常に改良や、新しい方法が研究されている。

中でも、十分に小さいサイズごとに異なるヒープから確保するようなメモリ管理の方法は、最近主流になりつつある。

メモリ管理の方法によっては、解放すべきアドレスだけ渡されて、サイズがわからない場合、まずサイズを特定するために、検索をかけるなどのコストのかかる処理が必要になる。

オブジェクトのサイズは、技術的にはコンパイラーが把握している。しかしこれまで、解放関数にサイズを伝える方法がなかった。

C++14では、サイズを引数に取る解放関数のオーバーロードが追加された。


void operator delete ( void * ptr, std::size_t size ) noexcept
{
    // sizeは解放すべきストレージのサイズ
}

これにより、一部のメモリ管理方法における解放処理が効率的に実装できるようになる。

クラスのメンバーとしては存在した解放関数のオーバーロードを、グローバルな解放関数にも導入したとも言える。

なおこの機能はClang 3.4で実装されている。GCCではまだ実装されていない。

Clang - C++1z, C++14, C++11 and C++98 Status

C++1y/C++14 Support in GCC - GNU Project - Free Software Foundation (FSF)

See Also:

本の虫: C++14の新機能: 2進数リテラル

本の虫: C++14の新機能: decltype(auto)

本の虫: C++14の新機能: 関数の戻り値の型推定

本の虫: C++14の新機能: 初期化lambdaキャプチャー

本の虫: C++14の新機能: 変数テンプレート

本の虫: C++14の新機能: constexpr関数の制限緩和

本の虫: C++14の新機能: メンバー初期化子と初期化リストの組み合わせ

本の虫: C++14の新機能: [[deprecated]] 属性

本の虫: C++14の新機能: 数値区切り

ドワンゴ広告

この記事はドワンゴ勤務中に書かれた。

今日は特に台風とは関係がなく午後に出社した。

ドワンゴは本物のC++プログラマーを募集しています。

採用情報|株式会社ドワンゴ

CC BY-ND 4.0: Creative Commons — Attribution-NoDerivatives 4.0 International — CC BY-ND 4.0

The Old New Thing: 実行ファイルのベースアドレスが0x00400000である理由

古参MS社員のRaymond Chenが、なぜWindowsのデフォルトのベースアドレスは0x00400000に配置されているのか。最初の4MBは何だという質問に答えている。

Why is 0x00400000 the default base address for an executable? - The Old New Thing - Site Home - MSDN Blogs

DLLのデフォルトのベースアドレスは0x10000000だが、なぜEXEのデフォルトのベースアドレスは0x00400000なのか。なぜこの値なのか。4メガバイトに何の意味があるのか。

これには、x86のディレクトリエントリひとつでマップできるアドレス空間であることと、1987年に決められた設計のためだ。

EXEのベースアドレスの制約としては、64KBの倍数であることのみだ。しかし、ベースアドレスを選ぶ理由には他にもある。

ベースアドレスを選ぶ目的は、モジュールが再配置される可能性を最小限に抑えることである。これはつまり、すでにそのアドレス空間にあるものと衝突させない(衝突にすると再配置しなければならない)ことと、後からそのアドレス空間に配置されるものと衝突しないということだ(後から来たものが再配置される)。実行可能ファイルにとって、後から配置されるものとの衝突を回避することとは、DLLによって占められる領域を回避することだ。OSはDLLを高アドレスに配置するし、OSのものではないDLLのデフォルトのベースアドレスは0x10000000なので、実行可能ファイルのベースアドレスは、0x10000000よりも低い場所であるべきで、低いほど、DLLと衝突するまでの領域が多くあることになる。しかし、どこまで低くできるのだろうか。

まず、すでにそこに配置されているものとの衝突を避けなけれあbならない。Windows NTは低アドレスにそれほどものを置かない。すでに配置してあるものは、nullポインターアクセスを補足するための、ゼロにマップしてあるPAGE_NOACCESSページだ。故に、Windows NTでは、実行可能ファイルのベースアドレスを0x00010000にすることができ、当時の多くのアプリケーションは実際にそうしていた。

しかし、Windows 95では、すでに多くのものが配置されていた。Windows 95のバーチャルマシンマネージャーは、CPUの不具合に対処するため、常に最初の64KBの物理メモリと最初の64KBのバーチャルメモリに配置されていた(Windows 95は、大量のCPUバグや、ファームウェアのバグに対処しなければならなかったのだ)。さらに、最初の1メガバイトのバーチャルアドレス空間は、バーチャルマシンの論理アドレス空間にまっぷされていた。(実際は、1メガバイトよりすこし多かった)。このマッピングは、x86プロセッサーのvirtual-8086モードに必要なものであった。

Windows 95は、その前身のWindows 3.1のように、Windowsを特別なバーチャルマシン上で動かしていて(System VMとして知られている)、互換性のために、あらゆるものを16bitコード上から通すようにしていた。偽物が正しく鳴くようにするためだ。そのため、CPUは(MS-DOSベースのアプリケーションではなく)Windowsアプリケーションを実行しているものの、バーチャルマシンのマッピングをいじしつづけているため、MS-DOS互換レイヤーのために毎回ページリマッピング(と、それに伴い高くつくTLBフラッシュ)を行う必要はなかった。

さて、最初の1メガバイトのアドレス空間はまな板の上から取り除かれているとして、他の3メガバイトはどうしたんだ。

さて、ここで記事の最初のヒントに戻る。

コンテキストスイッチを速くするため、Windows 3.1のバーチャルマシンマネージャーは、VMごとのコンテキストを4MBに切り上げていた。これにより、メモリーのコンテキストスイッチは、ページディレクトリのたったひとつの32bit値を書き変えるだけですんだ。(細かく言うと、インスタンスデータページもあるが、それは数十個程度のビットを反転させるだけだ)。この切り上げにより、3メガバイトのアドレス空間を失った。しかし、4ギガバイトものアドレス空間があるからには、十分の一パーセント以下の損失は、圧倒的なパフォーマンスの向上に比べれば、十分支払うべき価値のあるものだ(特に、この当時のアプリケーションで限界に挑むものなどなかった。当時のコンピューターは全体で2MBしかRAMがなかったのだから)

このメモリーマップは32-bit Windowsアプリケーションのための分離されたアドレス空間のための一部の変更がありながらも、Windows 95に引き継がれた。故に、実行可能ファイルがロードできる最も低いアドレスは4MBで、つまり0x00400000だ。

ギークトリビア:Win32アプリケーションがMS-DOS互換領域にアクセスするのを防ぐため、フラットデータセレクターは、実際には4MB境界で止まる下方に拡張するセレクターだ(同様に、16bit Windowsアプリケーションにおけるnullポインターは、nullセレクターが無効なため、アクセス違反を引き起こす。割り込みベクターテーブルにはアクセスしない)

リンカーは実行可能ファイルのデフォルトのベースアドレスとして0x0400000を選択するので、結果のバイナリはWindows NTとWindows 95の両方で再配置なしでロードできる。Windows 95対応はもはや誰も気にしないので、原理的には、リンカーの開発部署は、今ならば別のデフォルトベースアドレスを選べるはずである。しかし、グラフをちょっと綺麗にする以外の実質的な利点はない。それに、もし変えたとしたら、「なんである実行可能ファイルのベースアドレスには0x0400000のと、0x00010000のがあるんだ?」という質問が来るだけである。

またもや歴史の話。それにしても、今回のRaymond Chenの記事には、やたらとリンクがある。リンクされているページは、だいぶ昔のものが多いが、面白い。

2014-10-04

妖怪ハウスでボドゲ会の告知(10/25)

突然だが、10月24日は筆者の誕生日である。

いい機会なので、10月25日に、妖怪ハウスで大規模なボドゲ会を計画している。昼ごろから集まって、夜までのんびりとボードゲームをする予定だ。

また、ボドゲには興味がなかったとしても、酒と、うまいカレーを大量に用意するのでぜひ来てもらいたい。また、だいぶ作り慣れたスイーツである、ココナッツミルク寒天やドライフルーツヨーグルトアロエなども作ろうかと思う。ピザはどうしようか。

住所は、中野区野方5-30-13 ヴィラアテネ401となる。


View Larger Map

2014-10-03

C++14の新機能: 数値区切り

C++14の新機能、数値区切りは、整数リテラルと浮動小数点数リテラルを任意の桁で区切ることができる機能だ。区切り文字はシングルクオート。

int x = 1'000'000'000 ;
int y = 1'1'1'1'1'1 ;
int z = 1000000'00 ;

double d = 1'000.00 ;

ソースコードに10000000と100000000とが書かれていたとして、どちらが大きいかは、人間の目にはわかりにくい。しかし、10'000'000と100'000'000ならば、わかりやすい。

区切り桁は任意なので、日本人に馴染みやすい4桁で区切ることもできるし、2進数リテラルを8桁づつ区切ることもできる。

int n1 = 1'0000 ; // 1万
int n2 = 1'0000'0000 ; // 1億
// 8bit区切り
std::uint32_t bits = 0b00110100'10101010'00001111'11000011 ;

数値区切りは、純粋に人間がソースコードを目で読むための補助的な文法であって、プログラムの何らかの意味をもたらすものではない。

なおこの機能はGCC 4.9, Clang 3.4で実装されている。

Clang - C++1z, C++14, C++11 and C++98 Status

C++1y/C++14 Support in GCC - GNU Project - Free Software Foundation (FSF)

See Also:

本の虫: C++14の新機能: 2進数リテラル

本の虫: C++14の新機能: decltype(auto)

本の虫: C++14の新機能: 関数の戻り値の型推定

本の虫: C++14の新機能: 初期化lambdaキャプチャー

本の虫: C++14の新機能: 変数テンプレート

本の虫: C++14の新機能: constexpr関数の制限緩和

本の虫: C++14の新機能: メンバー初期化子と初期化リストの組み合わせ

本の虫: C++14の新機能: [[deprecated]] 属性

ドワンゴ広告

この記事はドワンゴ勤務中に書いた。

最近、ネタが尽きてきた。社内的には内定式という面白いイベントが先日あったそうだが、私は見ていなかったのでなんとも言えない。コスプレをしてきた内定者もいたという話だ。ネタ切れの時のために取っておいた、社内ヘアサロンに行ってみるべきだろうか。しかし、そのためには半年ほど髪を伸ばさなければならない。

ドワンゴは本物のC++プログラマーを募集しています。

採用情報|株式会社ドワンゴ

CC BY-ND 4.0: Creative Commons — Attribution-NoDerivatives 4.0 International — CC BY-ND 4.0

C++14の新機能: [[deprecated]] 属性

[[deprecated]]属性とは、C++11で追加された属性の文法で指定できる機能である。

deprecated属性は、任意の名前やエンティティに指定できる。deprecated属性を指定された名前やエンティティは、依然として通常通り使用できるが、使用が非推奨になる。

[[deprecated]] int handy_global_variable ;

int main()
{
    handy_global_variable ; // 非推奨
}

deprecated属性には、文字列リテラルを記述することもできる。

[[deprecated("Don't Use It!")]] int g ;

deprecated属性は、クラス、typedef名、変数、非staticデータメンバー、関数、enum、テンプレートの特殊化に対して指定できる。それぞれの属性を記述する文法は以下の通り。

// クラス
struct [[deprecated]] Class ;


// typedef名
[[deprecated]] typedef int typedef_name ;

// エイリアス宣言
using alias [[deprecated]] = int ;

// 変数
[[deprecated]] int variable ;
void f( [[deprecated]] int param ) ;

// 非staticデータメンバー
// staticデータメンバーは変数に含まれる
struct X
{
    [[deprecated]] static int data ;
} ;

// 関数
[[deprecated]] void function() ;

// enum
enum struct [[deprecated]] Enumeration { E } ;

// テンプレートの特殊化
template < typename T > struct Y { } ;

// Y<int>のみdeprecated
template < >
struct [[deprecated]] Y<int> { } ;

若干属性の文法がわかりにくいのは、クラスとenumとエイリアス宣言だろうか。

さて、規格上の解説はここまでで終わりだ。ここからは、規格外の解説になる。deprecated属性は、名前を非推奨だとマークできる機能だ。その情報はどのように使うのかということは、規格の範囲外である。

たとえば、コンパイラーは、deprecated属性の付与された名前が使われた場合、警告メッセージを出力する実装にできる。その際、指定した文字列リテラルも同時に出力するという実装もあり得る。

例えば、既存のC++実装であるGCCとClangでは、deprecated属性の付与された名前が使われた場合、警告メッセージを出力する。

なおこの機能はGCC 4.9, Clang 3.4で実装されている。

Clang - C++1z, C++14, C++11 and C++98 Status

C++1y/C++14 Support in GCC - GNU Project - Free Software Foundation (FSF)

See Also:

本の虫: C++14の新機能: 2進数リテラル

本の虫: C++14の新機能: decltype(auto)

本の虫: C++14の新機能: 関数の戻り値の型推定

本の虫: C++14の新機能: 初期化lambdaキャプチャー

本の虫: C++14の新機能: 変数テンプレート

本の虫: C++14の新機能: constexpr関数の制限緩和

ドワンゴ広告

この記事はドワンゴ勤務中に書かれた。

今夜は盛大にボドゲが行われる予定だ。

ドワンゴは本物のC++プログラマーを募集しています。

採用情報|株式会社ドワンゴ

CC BY-ND 4.0: Creative Commons — Attribution-NoDerivatives 4.0 International — CC BY-ND 4.0

2014-10-02

N1875: C言語にクラスを追加する提案

ask.fmで、面白い質問が来た。なんと、C言語にクラスを追加する提案論文が、先月末に公開されているというのだ。

N1876: Adding classes to C

C言語の標準規格は追っていないのだが、なかなか興味深い。

この提案は、C言語にC++風のクラス機能を追加する提案だ。ただし、C++のクラスをそのまま持ってくるのではなく、だいぶ保守的な採用の仕方をしている。

この論文で提案されているクラス機能は、C++の文法によく似ている。クラスstructかclassキーワードで宣言する。アクセス指定子もあり、structはデフォルトでpublic、classはデフォルトでprivateなのも、C++と同じだ。

派生はあるが、多重派生は認められていない。

virtual関数やRTTIはない。

また、C++にある、自動的に呼ばれるコンストラクター、デストラクターは存在しない。

かわりに、イニシャライザー、デリーターというものがある。これは、特定の名前を特別視する文法になるようだ。あるクラスClassNameがあるとして、イニシャライザーとデリーターは、それぞれ、initClassName, deleteClassNameと記述する。文法は、戻り値の型がない以外はメソッドと同じようだ。

struct Data
{
    int * ptr ;

    // 無引数イニシャライザー
    initData() { }

    // イニシャライザーはオーバーロードできる
    initData( int value )
    {   // thisキーワードはオブジェクトへのポインター
        ptr = (int *) malloc( sizeof(int) ) ;
        *ptr = value ;
    }

    // デリーター
    deleteData()
    {
        free( ptr ) ;
    }

    // デリーターもオーバーロードできる
    deleteData( int some_flags )
    {
        free ( ptr ) ;
    }

} ;

int main()
{
    Data d.initData( 123 ) ;

    // 明示的なデリーター呼び出しが必要
    // C++のように自動的に呼ばれることはない。
    d.deleteData() ;
}

派生は、単一派生のみ存在するが、イニシャライザーやデリーターが自動的に呼ばれることはない。呼ぶ必要があれば、明示的に呼ばなければならない。

struct CustomData : public Data
{
    
    initCustomData ()
    {
        // 基本クラスのイニシャライザーの呼び出しが必要であれば
        // 明示的に呼びださねばならない。
        initData( 123 ) ;
    }

    deleteCustomData()
    {
        // デリーターも同様
        deleteData( ) ;
    }
} ;

論文では、明示的なイニシャライザーとデリーターの呼び出しを、CUYOM (Clean Up Your Own Mess)、「テメーのケツはテメーで拭け」としている。いかにもC言語らしいシキタリであると言える。

C++と同じアクセス指定子が存在する。その意味もC++とまったく同じだ。


struct X
{
public :
    int x ;
protected :
    int y ;
private :
    int z ;
} ;

メンバー関数(論文ではメソッドという用語を使っている)の宣言と呼び出しは、C++と同じ文法で行う。

struct X
{
    void method() { }
} ;

void f( X x, X * p )
{
    x.method() ;
    p->method() ;
}

また、C++と同じように、thisポインターがある。

struct X
{
    int m ;
    void f()
    {
        // thisキーワードはX *型のポインター
        this->m = 0 ;
    }
} ;

論文では、C風の初期化もできるとしているが、以下のようなコードになっている。

struct Data
{
    int a ;
    int b ;
} ;

Data d = ( 1, 2 ) ;

また、途中のデータメンバーの初期化をスキップする文法も是非標準規格に入れたいと論文は書いている。


struct Data
{
    int a ;
    int b ;
    int c ;
} ;

// d.bの初期化は行われない
Data d = ( 1, , 3 ) ;

興味深いものの、C++畑の人間からすると、いろいろともやもやする部分が多い。

ドワンゴ広告

この記事はドワンゴ勤務中に書かれた。

ドワンゴは本物のC++プログラマーを募集しています。

採用情報|株式会社ドワンゴ

CC BY-ND 4.0: Creative Commons — Attribution-NoDerivatives 4.0 International — CC BY-ND 4.0

C++14の新機能: メンバー初期化子と初期化リストの組み合わせ

N3605: Member initializers and aggregates

さて、今回解説するC++14の新機能は、機能というよりは、変更といった方がいい小規模なものだ。そして、本来ならば、メンバー初期化子とアグリゲートというべきなのだが、わかりやすさのために初期化リストとしておいた。

C++では、特定の条件を満たしたクラスをアグリゲートと呼び、C言語から受け継いだメンバーごとの初期化ができる。

struct X
{
    int a ;
    int b ;
} ;

X x = { 1, 2 } ;
// x.a == 1
// x.b == 2

C++11には、メンバー初期化子という新機能が追加された。これは、クラスのデータメンバーの宣言に、初期化子を記述できる機能だ。

struct X
{
    int data = 0 ;
} ;

X x ; // x.data == 0

C++11では、この二つの初期化を組み合わせることができない。

// C++11ではエラー
struct X
{
    int a ;
    int b = 0 ;
} ;

// エラー
X x = { 0 } ;

これは、C++11の定義では、メンバー初期化子のあるクラスはアグリゲートではなくなるからだ。

C++14では、メンバー初期化子があってもクラスがアグリゲートの条件を満たすようにし、メンバー初期化子とアグリゲートによる初期化を組み合わせて使えるように、制限を緩和した。

struct X
{
    int a ;
    int b = 2 ; 
} ;

// C++14ではwell-formed
X x = { 1 } ;
// x.a == 1
// x.b == 2

これもconstexpr関数の制限緩和と同じで、プログラマーが難しいことを考えずに、普通に、自然に書いたコードがそのまま動くようにするための変更だ。

なおこの機能はClang 3.4で実装されている。GCCではまだ実装されていない。

Clang - C++1z, C++14, C++11 and C++98 Status

C++1y/C++14 Support in GCC - GNU Project - Free Software Foundation (FSF)

See Also:

本の虫: C++14の新機能: 2進数リテラル

本の虫: C++14の新機能: decltype(auto)

本の虫: C++14の新機能: 関数の戻り値の型推定

本の虫: C++14の新機能: 初期化lambdaキャプチャー

本の虫: C++14の新機能: 変数テンプレート

本の虫: C++14の新機能: constexpr関数の制限緩和

ドワンゴ広告

この記事もドワンゴ勤務中に書かれた。

今日は新機能というより、制限緩和ばかり紹介している。

ドワンゴは本物のC++プログラマーを募集しています。

採用情報|株式会社ドワンゴ

CC BY-ND 4.0: Creative Commons — Attribution-NoDerivatives 4.0 International — CC BY-ND 4.0

C++14の新機能: constexpr関数の制限緩和

C++14では、constexprの制限が大幅に緩和された。

C++11で追加されたconstexpr関数は、コンパイル時定数式として評価できる関数である。

constexpr std::size_t size()
{ return 10 ; }

// OK、size()は定数式
int a[size()] ;

しかし、C++11のconstexpr関数には、極めて厳しい制約がある。constexpr関数の本体は、実質、return文ひとつしか認められていないのだ。

プログラミングというのは、通常、変数や条件分岐やループといった機能を使う。constexpr関数では、それらの機能は実現可能ではあるが、C++としては極めて不自然な方法を用いなければならない。変数は引数に追い出し、条件分岐は条件演算子(?:)を使い、ループには再帰をしなければならない。

たとえば、変数、条件分岐、ループを使いたいsqrtを、C++11のconstexpr関数で書くと、以下のようになる。

// C++11のconstexpr関数によるsqrtの実装
template < typename T >
constexpr T sqrt_aux( T s, T x, T prev )
{
    return x != prev ? 
        sqrt_aux( s, ( x + s / x ) / 2.0, x ) : x ;
}

template < typename T >
constexpr T sqrt( T s )
{ return sqrt_aux( s, s/2.0, s ) ; }

C++14では、constexpr関数の制約が大幅に緩和された。C++14のconstexpr関数では、sqrtは以下のように書くことができる。

template < typename T >
constexpr T sqrt( T s )
{
    T x = s / 2.0 ;
    T prev = 0.0 ;

    while ( x != prev )
    {
        prev = x ;
        x = (x + s / x ) / 2.0 ;
    }
    return x ;
}

C++14のconstexpr関数は、変数の宣言ができる。ただし、必ず初期化されなければならず、staticとthread_local変数は宣言できない。


constexpr int f( int x )
{
    int l = x ; // OK

    int e1 ; // エラー、未初期化
    static int e2 ; // エラー
    thread_local int e3 ; // エラー

    return 0 ; 
}

変数を変更することもできる。変更できる変数は、定数式の評価とともに寿命が始まったものだけだ。

constexpr int f( int x )
{
    int l = x ;
    l += 1 ;
    ++l ;
    --l ;

    return l ;
}

条件分岐として、if文とswitch文を使うことができる。goto文は使えない。

ループとして、for, while, do whileを使うことができる。

まとめとして、一言で言えば、C++14のconstexpr関数は、普通に書けるようになったということだ。

なおこの機能はClang 3.4で実装されている。GCCではまだ実装されていない。

Clang - C++1z, C++14, C++11 and C++98 Status

C++1y/C++14 Support in GCC - GNU Project - Free Software Foundation (FSF)

See Also:

本の虫: C++14の新機能: 2進数リテラル

本の虫: C++14の新機能: decltype(auto)

本の虫: C++14の新機能: 関数の戻り値の型推定

本の虫: C++14の新機能: 初期化lambdaキャプチャー

本の虫: C++14の新機能: 変数テンプレート

ドワンゴ広告

この記事はドワンゴ勤務中に書かれた。

そういえば、最近、社内で昼ボドゲをしていない。金曜の夜は大抵ボドゲが行われているが。

ドワンゴは本物のC++プログラマーを募集しています。

採用情報|株式会社ドワンゴ

CC BY-ND 4.0: Creative Commons — Attribution-NoDerivatives 4.0 International — CC BY-ND 4.0

2014-10-01

C++14の新機能: 変数テンプレート

C++14に追加された新機能、変数テンプレートは、名前の通り、変数をテンプレート宣言する機能である。

変数テンプレートのコードをいきなり読むと、あるいは混乱するかもしれないので、変数テンプレートについて解説する前に、まず、テンプレートについて解説しようと思う。

C++には、関数というものがある。関数には、具体的な型を書く必要がある。

int min_int( int a, int b )
{
    return a < b ? a : b ;
}

double min_double( double a, double b )
{
    return a < b ? a : b ;
}

これをみると、型以外は、同一のコードである。もし、型を引数に取ることができれば、コピーコンストラクター、operator <, デストラクターのような操作ができるすべての型に対して、共通のコードで対応できることになる。そのために、C++には関数テンプレートがある。

template < typename T >
T min( T const & a, T const & b )
{
    return a < b ? a : b ;
}

また、C++には、クラスというものがある。クラスには、具体的な型を書く必要がある。

class linked_list_int
{
    linked_list_int * next ;
    linked_list_int * prev ;
    int value ;
} ;

class linked_list_double
{
    linked_list_double * next ;
    linked_list_double * prev ;
    double data ;
} ;

関数の場合と同じように、型以外は、同一のコードである。関数テンプレートと同じように、クラステンプレートは、型を引数に取り、コードの共通化を行える。

template < typename T >
class linked_list
{
    linked_list * next ;
    linked_list * prev ;
    T data ;
}

いまさら何を初歩的なことを解説しているのかと思うかもしれないが、テンプレートは型を引数化するということがわかったことと思う。

ところで、C++には変数というものがある。変数には、具体的な型を書く必要がある。

constexpr int pi_int = 3 ;
constexpr double pi_double = 3.141592 ;

これを見ると、型以外は共通化できる。C++14の変数テンプレートは、変数にテンプレート宣言ができる。

template < typename T >
constexpr T pi = static_cast<T>(3.141592) ;

int x = pi<int> ;
double y = pi<double> ;

なぜ関数テンプレートではダメなのか。関数テンプレートでもいいのだが、文法上、余計な関数呼び出し式()が必要だ。

template < typename T >
constexpr T pi() { return static_cast<T>(3.141592) ; }

int x = pi<int>() ;
double y = pi<double>() ;

プログラマーは冗長な文法を嫌うものなので、変数テンプレートが導入された。

ちなみに、変数テンプレートには、かなり有益な使い方がある。既存の値を返すtype traitsは、valueという名前のstaticデータメンバーを使っていた。

bool b = std::is_pointer<T>::value ;

変数テンプレートを使えば、以下のようにラップすることができる。

template < typename T >
constexpr bool is_pointer_v = is_pointer<T>::value ;

bool b = is_pointer_v<T> ;

C++14では、標準ライブラリに、このようにして既存のtype traitsをラップする_v版が入った。

なおこの機能はGCC 5とClang 3.4で実装されている。

Clang - C++1z, C++14, C++11 and C++98 Status

C++1y/C++14 Support in GCC - GNU Project - Free Software Foundation (FSF)

See Also:

本の虫: C++14の新機能: 2進数リテラル

本の虫: C++14の新機能: decltype(auto)

本の虫: C++14の新機能: 関数の戻り値の型推定

本の虫: C++14の新機能: 初期化lambdaキャプチャー

ドワンゴ広告

この記事はドワンゴ勤務中に書かれた。

どうやら、いつの間にかドワンゴに入社して7ヶ月が経過しているようだ。

ドワンゴは本物のC++プログラマーを募集しています。

採用情報|株式会社ドワンゴ

CC BY-ND 4.0: Creative Commons — Attribution-NoDerivatives 4.0 International — CC BY-ND 4.0

2014-09-30

C++14の新機能: ジェネリックlambda

C++14に追加された新機能に、ジェネリックlambdaがある。

lambda式は便利だ。lambda式が便利な理由は、簡潔に書けるからだ。しかし、C++11のlambda式は、引数の型を書かなければならないので、十分に簡潔に書くことはできない。

struct very_long_name
{
    int x ;
    double y ;
    std::string z ;
} ;

void sort( std::vector<very_long_name> & v )
{
    std::sort( v.begin(), v.end(),
        []( very_long_name & a, very_long_name & b )
        {   // 複数のデータメンバーを正しく比較する方法を覚えておくと、
            // いつかためになる。
            return std::tie( a.x, a.y, a.z ) < std::tie( b.x, b.y, b.z ) ;
        } ) ;
}

引数の型ぐらい、コンパイラーが何とかしてほしいものである。そこで、C++14には、引数の型を書かずにすむ、ジェネリックlambdaが追加された。

    std::sort( v.begin(), v.end(),
        []( auto & a, auto & b )
        {
            return std::tie( a.x, a.y, a.z ) < std::tie( b.x, b.y, b.z ) ;
        } ) ;

これだけでは、単に引数の型を省略出来るだけに見えるかもしれない。以下の例で、ジェネリックlambdaの素晴らしさがわかるだろう。

template < typename print >
void f( print p )
{
    p( 42 ) ;
    p( 3.14 ) ;
    p( "hello" ) ;
}

int main()
{
    f( []( auto && elem ) { std::cout << elem << '\n' ; } ) ;
}

ジェネリックlambdaは、テンプレートのようにどんな型でも受け付ける。

lambda式は魔法ではなく、クロージャーオブジェクトと呼ばれる、単なる関数オブジェクトを返すシンタックスシュガーに過ぎない。ジェレリックlambda式も同じだ。テンプレートのようにと書いたが、実際、メンバーテンプレートを持つ関数オブジェクトのシンタックスシュガーに過ぎない。例えば、上記のlambda式によって生成されるクロージャーオブジェクトは、以下のようになる。


struct closure_object
{
    template < typename T >
    void operator ( T && elem ) const
    { std::cout << elem << '\n' ; }
} ;

メンバーテンプレートを使えば、printに与える適切な関数オブジェクトを記述することができるが、C++11のlambda式には、これを可能にする文法がなかった。C++14で、ジェネリックlambdaとして追加された。

なおこの機能はGCC 4.9とClang 3.4で実装されている。

Clang - C++1z, C++14, C++11 and C++98 Status

C++1y/C++14 Support in GCC - GNU Project - Free Software Foundation (FSF)

See Also:

本の虫: C++14の新機能: 2進数リテラル

本の虫: C++14の新機能: decltype(auto)

本の虫: C++14の新機能: 関数の戻り値の型推定

本の虫: C++14の新機能: 初期化lambdaキャプチャー

ドワンゴ広告

この記事はドワンゴ勤務中に書かれた。

次の論文集がでるのは10月半ばになるので、それまではC++の解説記事を書く。

ドワンゴは本物のC++プログラマーを募集しています。

採用情報|株式会社ドワンゴ

CC BY-ND 4.0: Creative Commons — Attribution-NoDerivatives 4.0 International — CC BY-ND 4.0

C++14の新機能: 初期化lambdaキャプチャー

C++14で追加された新機能を解説していくシリーズ第四弾。今回は、初期化lambdaキャプチャーを解説する。これは、提案では、汎用lambdaキャプチャーと呼ばれていたが、どうやら今は、もっとわかりやすい、初期化lambdaキャプチャーとも呼ばれているようだ。

C++14の初期化lambdaキャプチャーについて解説する前に、まず、C++11のlambdaキャプチャーと、その問題点について解説しなければならない。

lambda式は、自動ストレージ上にあるオブジェクトか、thisをキャプチャできる。

void f()
{
    int x = 0 ;

    // コピーキャプチャ
    [=]{ return x ; } ;
    // リファレンスキャプチャ
    [&]{ x = 1 ; } ;
}

lambda式は、クロージャーオブジェクトという未規定の型のオブジェクトを生成する。lambda式がローカル変数をキャプチャするのは、魔法ではなく、単なる関数オブジェクトのシンタックスシュガーに過ぎない。

たとえば、以下のようなlambda式には、

void f()
{
    int x = 0 ;
    [=]{ return x ; } ;
}

以下のようなクロージャーオブジェクトが生成される。

class closure_object
{
    int const x ;
public :
    closure_object( int x )
        x(x) { }
    int operator()
    {
        return x ;
    }
} ;

以下のようなlambda式には、

void f()
{
    int x = 0 ;
    [&]{ return x ; } ;
}

以下のようなクロージャーオブジェクトが生成される。

class closure_object
{
    int & x ;
public :
    closure_object( int & x )
        x(x) { }
    int operator()
    {
        return x ;
    }
} ;

これをみると、lambda式が魔法でも何でもなく、従来のC++でもできたことの、シンタックスシュガーに過ぎないことが分かるだろう。

では、何が問題なのか。C++11のlambdaキャプチャーには、二つの問題がある。

データメンバーのキャプチャーができない。

C++11のlambda式は、クラスのデータメンバーのキャプチャーができない。と、こう書くと、読者は以下のようなコードが手持ちのコンパイラーでコンパイルでき、その実行結果も意図通りであることを持って反論するかもしれない。

struct X
{
    int data_member = 0 ;

    void f()
    {
        auto l = [=](){ return data_member ; } ; 
        l() ;
    }

} ;

int main()
{
    X x ;
    x.f() ;    
}

なるほど、たしかにこのコードを読むと、lambda式がデータメンバーをキャプチャーしているように見える。しかし、実際にはdata_memberをキャプチャーしているわけではない。lambda式がキャプチャーしているのは、thisである。data_memberはthis経由で使われているに過ぎない。上のlambda式によって生成されるクロージャーオブジェクトは、以下のようになる。

class closure_object
{
    X * this_ptr ;
public :
    closure_object( X * this_ptr )
        : this_ptr(this_ptr) { }

    int operator ()
    {
        return this_ptr->data_member ;
    }
} ;

data_memberはthisポインター経由でアクセスしているだけで、data_memberをキャプチャーしているわけではない。data_memberはthisポインター経由の間接参照をされるため、コピーされていない。したがって、以下のコードは正しく動かない。

struct X
{
    int data = 0 ;
    void get_lambda()
    {   // コピーキャプチャーしているつもり
        return [=](){ return data ; }
    }
} ;

int main()
{
    std::function< int() > f ;

    {
        X x ;
        f = x.get_lambda ;
    }// xは破棄されている

    f() ; // 挙動は未定義
}

データメンバーはthisポインターを経由して間接参照されているため、thisの参照先が破棄された後では、挙動は未定義となる。

したがって、データメンバーをコピーキャプチャーするには、以下のようにマヌケで冗長な記述をしなければならない。

struct X
{
    int data = 0 ;
    void get_lambda()
    {
        int data = this->data ;
        return [=](){ return data ; }
    }
} ;

ムーブキャプチャーができない。

C++11のlambda式では、変数をムーブしてキャプチャーすることができない。

auto f()
{
    std::vector<int> v(1000) ;

    return [=]() { return std::make_pair( v.begin(), v.end() ) ; }
}

この例で、vは関数fから呼び出し元に戻れば破棄されるオブジェクトである。しかし、lambda式のクロージャーオブジェクトは、関数の呼び出し元に返さなければならないので、リファレンスキャプチャーすることはできない。コピーをしなければならないが、どうせ関数はすぐ呼び出し元に帰るので、変数vはもう必要ないのに、コピーが発生してしまう。ムーブをしたいところだが、C++11のlambda式には、そのための文法がない。

ムーブキャプチャーできない制限は、C++11の策定時に認識されていたが、適切な文法の議論が必要なために、C++11では後回しにされた。データメンバーをコピーキャプチャーできない問題は、C++11を教育する際に、学習者からわかりにくい挙動だと指摘された。

C++14では、この問題を解決するために、初期化lambdaキャプチャーを追加した。これは、lambdaキャプチャーに初期化子を書くことができる機能だ。


void f()
{
    int data = 0 ;

    // コピーキャプチャー
    [ data = data ]{} ;
    // リファレンスキャプチャー
    [ &data = data ]{ } ;
}

このように、キャプチャーの識別子に初期化子を書くことができる。

初期化lambdaキャプチャーは、あたかも、"auto 初期化lambdaキャプチャー"と書いたかのように振る舞う。リファレンスキャプチャーをする場合は、名前の前に&を書く。

初期化キャプチャーでは、名前を好きにつけることができる。


void f()
{
    int very_long_name = 0 ;
    [ i = very_long_name ] { } ;
}

また、初期化子は初期化子なので、好きな式を書ける。そのため、キャプチャーというよりは、クロージャーオブジェクトのデータメンバーの宣言に近い。


// 自動ストレージ上にはないオブジェクト
int a = 0 ;

int get_value() ;

void f()
{
    int b = 0 ;

    [ a = a, b = b + 1, c = 1, d = get_value() ] { } ;
}

この機能を使うと、C++11のlambdaキャプチャーにある二つの問題を解決できる。

データメンバーのコピーキャプチャーは、簡単にできる。

struct X
{
    int data = 0 ;
    void get_lambda()
    {   // コピーキャプチャー
        return [ data = data ](){ return data ; }
    }
} ;

int main()
{
    std::function< int() > f ;

    {
        X x ;
        f = x.get_lambda ;
    }// xは破棄されている

    f() ; // OK、
}

また、初期化子を書けるので、ムーブをするコピーキャプチャーも書ける。

auto f()
{
    std::vector<int> v(1000) ;

    return [ v = std::move(v) ]() { return std::make_pair( v.begin(), v.end() ) ; }
}

初期化lambdaキャプチャーは、lambda式をより便利に使うことができる新機能だ。

なおこの機能はGCC 4.9とClang 3.4で実装されている。

Clang - C++1z, C++14, C++11 and C++98 Status

C++1y/C++14 Support in GCC - GNU Project - Free Software Foundation (FSF)

See Also:

本の虫: C++14の新機能: 2進数リテラル

本の虫: C++14の新機能: decltype(auto)

本の虫: C++14の新機能: 関数の戻り値の型推定

ドワンゴ広告

この記事はドワンゴ勤務中に書かれた。

最近、ドワンゴ社内で売られている昼食の質が向上した。好きなだけ取ってグラム単位で会計する仕組みの昼食販売が導入されたからだ。

野菜が取り放題で、しかも安いので、すばらしい。

さて、筆者が十分に多く盛りつけたサラダを食べて満足していると、いかにもよく肉を食べそうな図体のドワンゴ社員が、「僕、肉しか食べないんで」と言いながら、実際に肉を大量に盛り付けて満足そうにしていた。有限実行の徒である。

ドワンゴは本物のC++プログラマーを募集しています。

採用情報|株式会社ドワンゴ

CC BY-ND 4.0: Creative Commons — Attribution-NoDerivatives 4.0 International — CC BY-ND 4.0

2014-09-29

妖怪ハウスでスタッフドチキンを作った

本の虫: 妖怪ハウスに不思議なものが送られてきたで報告したように、妖怪ハウスに鶏が丸ごと冷凍で送られてきた。せっかく丸ごとの鶏なので、ふさわしい料理をしたいものだ。しかし、うまくできるだろうか。

今日、意を決して、スタッフドチキンを作ることに決めた。

まず、冷凍の丸鶏を冷蔵庫に入れて一日かけて回答する。解凍できたら、水洗いして、表面に塩コショウをまぶす。

詰め物としては、タマネギと人参とセロリをみじん切りして炒め、数時間水につけておいたもち米を入れて、塩コショウとコンソメスープで煮込んだものを詰め込んだ。

チキンの表面にオリーブオイルをぬる。

オーブンの板に人参とセロリを敷き詰め、チキンを置いて、オーブンで加熱した。

無事出来上がり、なかなかの好評を得た。

スタッフドチキンは、見た目が豪華そうでよい。ただし、食べるために切り分けるのが手間だった。

今回得た経験から考えると、敷き詰める野菜にもっとこだわるべきであった。セロリの葉はこげてしまうので、敷き詰めても意味がなかった。今度からは茎だけにするつもりだ。人参はもっと大き目に切るべきだった。また、じゃがいもやかぼちゃを置くのもいいだろう。また、焼き方も、低温で長時間焼くのがよいだろう。せっかちな住人に度々焼いている途中でオーブンを開けられて、加熱が中断されたのが残念だった。

また作ろうと思う。クリスマスに作るのが、一番ふさわしいのではないかと思う。クリスマスといえば七面鳥だが、七面鳥は高いし、また調理にも時間がかかるので、日本ではチキンのほうが手軽だろう。

C++14の新機能: 関数の戻り値の型推定

C++14では、通常の関数に、戻り値の型推定という機能が加わった。

C++14の戻り値の型推定を解説する前に、まずC++11の話を使用。

戻り値の型推定は、C++11のlambda式に備わっていた。ただし、C++11では、lambda式が戻り値の型推定を行うためには、本体はreturn文ひとつだけでなければならないという制約があった。return文ひとつだけでない場合、戻り値の型はvoid型となる。

// C++11のlambda式
// 戻り値の型はint
[](){ return 0 ; } ;

// 戻り値の型はvoid
[]() { } ;


// ill-formed.
// 戻り値の型はvoidなのにint型を返している。
[]()
{
    int x = 0 ;
    return x ;
} ;

C++14では、通常の関数の戻り値の型推定機能の導入と共に、そのような制限は撤廃された。

// C++14のlambda式
// well-formed
[]()
{
    int x = 0 ;
    return x ; 
} ;

また、C++11では、placeholder typeという機能が導入された。

// C++11のplaceholder type
auto x = 0 ; // int

C++14では、placeholder typeに、decltype(auto)が追加された。

// C++14のplaceholer type
decltype(auto) x = 0 ; // int

decltype(auto)とautoの違いについては、前回の解説を参照。

本の虫: C++14の新機能: decltype(auto)

C++14に追加された通常の関数の戻り値の型推定は、このplaceholder typeを関数の戻り値の型として指定できる。その場合、型はreturn文のオペランドの式から推定される。

// C++14の戻り値の型推定

// void
auto f() { }

// int
auto g() { return 0 ; }

// int
decltype(auto) h() { return 0 ; }

戻り値の型としてのplaceholder typeは、後置することもできる。

auto f() -> auto
{
    return 0 ;
}

この文法が認められている理由は、主に、lambda式にplaceholder typeを記述するためだ。


// int &
[]() -> auto & 
{
    static int x ;
    return x ;
} ;

decltype(auto)は、主に戻り値の型推定を行わせるために追加された。

int & f() ;

// int
auto g() { return f() ; }

// int &
decltype(auto) h() { return f() ; }

C++14の戻り値の型推定には、C++11のlambda式の戻り値の型推定のような、とても使いづらい制限はない。普通に書けば普通に動く。ただし、細かい点で注意すべきところはある。

すべてのreturn文のオペランドの式の型は、一致していなければならない。

auto f()
{
    return 0 ;
    return 0.0 ; // ill-formed. 型の不一致
}

再帰はできる。もちろん、return文のオペランドの式の型は、すべて一致していなければならない。


auto ackermann( int m, int n )
{
    if ( m == 0 )
        return n + 1 ;
    if ( n == 0 )
        return ackermann( m - 1, 1 ) ;
    else
        return ackermann( m - 1, ackermann( m, n-1 ) ) ;
}

placeholder typeの型推定にあたって、まだ型推定されていないplaceholder typeが現れる場合は、エラーとなる。これは、変数の場合と同等だ。


auto x = x ; // エラー

auto f() ;
// エラー
auto g() { return f() ; }

placeholder typeを使った関数を再宣言する場合は、同じplaceholder typeを使わなければならない。推定される具体的な型を使うことはできない。

auto f() ; // OK、宣言
auto f() { return 0 ; } // OK、定義

auto f() ; // OK、再宣言

// ill-formed
// 同じplaceholer typeを使わなければならない
decltype(auto) f() ;
int f() ;

関数テンプレートの明示的実体化や明示的特殊化でも、同じplaceholder typeを使わなければならない。

// #1
template < typename T > auto f( T t ) { return t ; }

// 明示的実体化
extern template auto f( int ) ; // OK
extern template char f( char ) ; // エラー

// 明示的特殊化
template < > auto f( short ) ; // OK
template < > long f( long ) ; // エラー

// #2
// #2は#1とは異なるテンプレートであることに注意
template < typename T > T f( T t ) { return t ; }

extern template char f( char ) ; // OK, #2の明示的実体化
template < > long f( long ) ; // OK、#2の明示的特殊化

もちろん、上記のテンプレートは、実際に実体化して使う際に曖昧になるが、それは使う場合の話であって、テンプレートの話ではない。

virtual関数で戻り値の型推定機能を使うことはできない。提案論文のN3638によれば、技術上可能ではあるが、オーバーライドのチェックとvtableレイアウトが複雑化するため、現時点では禁止しておくとのことだ。

戻り値の型推定で、std::initializer_listを推定することはできない。

placeholder typeで変数を宣言する場合は、std::initalizer_listを推定することができるが、std::initializer_listは実装の都合上、自動ストレージ上に構築して参照渡しをするため、関数の戻り値として返すのは不適切であるという理由に寄る、

// OK
// std::initializer_list<int>
auto x = { 1, 2, 3 } ;

// エラー
// std::initializer_listは推定できない
auto f()
{
    return { 1, 2, 3 } ;
}

以上、細かい点はあるが、通常の利用では、それほど気にする必要はない。普通に書けば、自然に動くように設計されているはずだ。

See Also:

本の虫: C++14の新機能: 2進数リテラル

本の虫: C++14の新機能: decltype(auto)

ドワンゴ広告

ドワンゴは本物のC++プログラマーを募集しています。

採用情報|株式会社ドワンゴ

CC BY-ND 4.0: Creative Commons — Attribution-NoDerivatives 4.0 International — CC BY-ND 4.0