2015-12-01

C++標準化委員会の文書のレビュー: P0090R0-P0099R0

P0090R0: Removing result_type, etc.

result_type, argument_type, first_argument_type, second_argument_typeというネストされた型名の廃止。

これらのネストされた型名はdecltype以前の機能であり、もはやその役割は別の機能(result_ofなどのtraits)で担うことができる。また、labmda式のクロージャーオブジェクトなどresult_typeを持たない型が多数あり、もはやresult_typeなどを使うコードは、ジェネリックではない。

ただし、<random>にあるresult_typeなどは意味があるため、残す。

not1, not2の廃止。これらはVariadic Templates以前の機能であり、いまはより優れたnot_fnで代替できる。

これらの廃止した項目を、規格の中で互換機能を記述する箇所である、Annex C: Compatibilityに移動。

互換性のために、これらの除去した機能を実装するコンパイラーはC++17規格準拠であるという例外的な文面を追加する。

P0091R0: Template parameter deduction for constructors (Rev. 2)

関数テンプレートにある実引数推定(argument deduction)をクラステンプレートのコンストラクターにも提供する提案。

関数テンプレートには、テンプレート実引数を関数の実引数から推定してくれる機能がある。

template < typename T >
void f( T t ) ;

int main()
{
    f( 0 ) ; // f<int>
    f( 0.0 ) ; // f<double>
}

しかし、クラステンプレートにはそのような機能はないため、テンプレート実引数を手書きしなければならない。

template < typename T >
struct X
{
    X( T t ) ;
} ;

int main()
{
    X x(0) ; // エラー
    X<int> x(0) ; // OK
}

この問題に対する伝統的な解決方法は、関数テンプレートに実引数推定させてクラステンプレートを返す方法がある。

template < typename T >
X<T> make_x( T && t )
{
    return X<T>( std::forward<T>(t) ) ;
}

int main()
{
    auto x = make_x( 0 ) ; // X<int>
}

この解決方法の問題は、まずmake_x関数テンプレートを書かなければならないということだ。ライブラリごとにそのようなmake関数のインターフェースは異なるので、ライブラリごとにmake関数を使う際にドキュメントを参照しなければならない。コピーもムーブもできない型に対してはmake関数を提供できない。

この提案による機能は2つある。まずは、クラステンプレートのコンストラクターが型を推定する機能

pair p(1, 1.0 ) ; // pair<int, double>
tuple t( 1, 1.0, "hello" ) ; // tuple<int, double, const char * >

もうひとつは、コンストラクターがクラステンプレートのテンプレート実引数に依存していない場合でも、実引数推定を行えるようにする機能。

例えば、vectorのイテレーターを取るコンストラクターは、上記の方法では実引数推定できない。

template < typename T, typename Allocator = std::allocator<T> >
class vector
{
    template < typename InputIterator >
    vector( InputIterator first, InputIterator last ) ;
} ;

このため、コンストラクターからクラステンプレートのテンプレート仮引数に依存を発生させ、実引数推定をさせるためのマッピング機能が提案されている。

提案されているマッピングの文法案

template<typename T, typename Alloc = std::allocator<T>> struct vector {
  /* ... */
};
template<typename InputIterator>
vector(InputIterator first, InputIterator last)
-> vector<typename iterator_traits<InputIterator>::value_type>

極めて読みづらい文法だ。これはライブラリ作者が書くものであって、単なるライブラリのユーザーは存在を気にする必要はないのが救いか。

P0092R0: Polishing chrono

<chrono>を改良する提案

time_pointにインクリメント演算子とデクリメント演算子のオーバーロードを追加

durationとtime_pointに新しい丸めモードを追加、floor(負の無限大方向に丸める)、ceil(正の無限大方向に丸める)、round(近い方に丸める)

符号付きdurationにabsを提供

P0093R0: Simply a Strong Variant

ダブルバッファリングを含む対策をすることでnever empty保証のある強いvariantの提案。

never empty保証をするためには、ダブルバッファリングが必要になる。これはvariantのサイズを2倍にしてしまう。

論文は、プログラマーはコストを犠牲にしてもプログラムの正確性を重視すると主張している。また、無例外保証のあるムーブを提供している型だけ渡されたならば、メタプログラミングによりダブルバッファリングを回避する実装が可能であること、Transactional Memoryの発展によって、将来はあらゆる型でダブルバッファリングをしなくてもすむようになる(かなり疑わしいが)ので、将来の拡張性があることなどを挙げている。

P0094R0: Simply a Basic Variant

P0093の省略版のような文書。N0093とほぼ同じ。

P0095R0: The Case for a Language Based Variant

コア言語でvariantに対応する提案。

純粋にライブラリベースのvariant実装は使いづらい。一般ユーザーにすらSFINAEによるコンパイル時条件分岐を強いる。

そこで、コア言語による対応が必要だとしている。

提案では、以下のようにvariantを宣言できる。

enum union param
{
    int age ;
    std::string name ;
    std::string address ;
} ;

コア言語で対応するので、switch文もvariantに対応できる。

struct Person
{
    int age ;
    std::string name ;
    std::string address ;

    void set( param p )
    {
        switch( p )
        {
            case age a :
                age_ = a ;
                break ;
            case name n :
                name = n ;
                break ;
            case address a :
                address = a ;
                break ;
        }
    }
} ;

int main()
{
    Person p ;
    p.set( param::name("Ada") ) ;
}

やはりコア言語で対応すると使いやすい。

P0096R0: Feature-testing recommendations for C++

プリプロセッサーマクロによる機能テストのためのライブラリ。C++17機能に対応する機能テストが追加されている。

P0097R0: Use Cases for Thread-Local Storage

TLSの利用例を示した文書。

スレッドローカルストレージには長年の歴史があるが、近代的な並列実行において、利用価値はあるのかどうか疑問視されることがある。この文書は、TLSは現代でも利用価値があると解説している。

問題は、SIMDやGPGPUではTLSはサポートしづらい。しかし、TLSがサポートされないのでは有益なコードが書きにくい。

提案では、SIMDやGPGPUという実行媒体の特性を解説した上で、TLSを現実的にサポートするための案をいくつか出している。たとえば、TLSに複数の種類を作る案。SIMDやGPGPUで使うTLSは全TLSの一部なので、その一部だけを絞り込めれば実用的なパフォーマンスになる。あるいは、SIMDやGPGPUのTLSは、非トリビアルなコンストラクターが実行されない制約をつけるなど。

RCUの解説。

[PDF] P0099R0: A low-level API for stackful context switching

N4397の改訂版。

タイトル通り、stackful context switchのための低級APIの提案。

ドワンゴ広告

ドワンゴはプログラミング講師を募集しているそうだ。

【教育事業】プログラミング講座 講師候補(正社員)|募集職種一覧|採用情報|株式会社ドワンゴ

応募にあたっての課題が、ドワンゴ プログラミング講師候補採用試験問題に書いてある。

正解が「オブジェクト指向プログラミング」となる問題文を作成せよという課題が興味深い。「最大200文字以内」という条件はいいとして、「正答の直接的な記載を禁ずる」ことと、「プログラミング言語名の記載を禁ずる」という条件がある。

筆者の頭にとっさに浮かんだ、まったく試験にふさわしくない問題分は、「Alan Kayが研究していたプログラミングのパラダイム」とか、「UMLで記述されるプログラミングのパラダイム」である。Alan Kayといえばわかりやすいが、しかしこれは歴史問題のようだ。UMLは一般にプログラミング言語とは認識されていないので、当然条件を満たすはずだ。

それはそうと、C++のオブジェクト指向は、Simulaの影響を受けている。オブジェクト指向という言葉を使った有名人はAlan Kayで、Alan Kay自身もプログラミング言語を開発しているのだが、C++を始めとする真面目な実務向きのプログラミング言語におけるオブジェクト指向のサポートは、Simulaの影響が大きい。

Alan Kay自身のオブジェクト指向を具体化したプログラミング言語の実装、Smalltalkは、実務向けのプログラミングにはあまり影響を与えなかった。Objective-Cや、あまり知らないがひょっとしたらSwiftがその影響を受けているかもしれない。Smalltalkは、単なるプログラミング言語ではなく、smalltalkというシェルでもあり、エディタでもある、プログラミング環境であり、プログラミング言語単体で切り離せなかったため、実務からは遠くなってしまったのだと思う。

ところで、最近のC++は、というよりも最近の言語は、あまりオブジェクト指向対応を謳っていない気がする。これは、いまさらに構造化プログラミング対応言語を謳わないのと同じで、オブジェクト指向が十分に一般化したためではないかと思う。

また、オブジェクト指向のプログラミング言語での取り入れ方もだいぶ変わった。オブジェクト指向の黎明期は、やたらと派生と継承ベースの設計が流行ったが、複雑な派生関係をユーザー側に露出させてしまうと、ライブラリが使いづらくなる。

最近は汎用的なインターフェース(制約)によるライブラリ設計が多いようだ。コンセプトが議論されているのもそのためだ。

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

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

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

2 comments:

Anonymous said...

今回のドワンゴ広告、広告ではあるのだが長いし
面白いので、独立した記事に意見をまとめた方がいいのでは。

Anonymous said...

手続きとデータを一つの塊として扱う機構を一般的になんというか。またそのようなプログラミング手法をなんというか。
うーん。難しい。

基本的にヴァリアントで必要な型というのは整数型、浮動小数型、文字列、汎用的なクラスオブジェクトの4つのMixだと思うのですが、基本言語に組み込むほどのものかというとそうでもないような気がします。