本の虫

著者:江添亮
ブログ: http://cpplover.blogspot.jp/
メール: boostcpp@gmail.com
Twitter: https://twitter.com/EzoeRyou
GitHub: https://github.com/EzoeRyou

アマゾンの江添のほしい物リストを著者に送るとブログ記事のネタになる

筆者にブログのネタになる品物を直接送りたい場合、住所をメールで質問してください。

C++標準化委員会の文書: P0301R0-P0309R0

P0301R1: P0301R1: Wording for Unified Call Syntax (revision 1)

統一関数呼び出し記法

f(x,y)という式に対して、まずf(x,y)が妥当なフリー関数fを探し、見つからなければ、x.f(y)が妥当となるxのメンバー関数fを探す機能がまずひとつ。

この提案は、さらにUnified call introducer(統一呼び出し導入子)という文法により、メンバー関数呼び出しからフリー関数を呼び出す機能も提案している。

もともと、上の機能と同じ考え方で、x.f(y)という式に対してf(x,y)と同じ意味を持たせたかったが、様々な理由により没になった。そこで、既存の式とは異なる新しい文法を追加すればそのような機能が追加できるという発想で、unified call introducerの文法が追加された。その文法は.だ。

以下がunified call introducerを使った式となる。

.f(x,y)
.x.f(y)

行頭が.で始まっているが、これはtypoではない。これがunified call introducerだ。

.f(x,y)は、f(x,y)が妥当なフリー関数fの集合と、x.f(y)が妥当なメンバー関数fの集合からなる関数fの集合に対して、オーバーロード解決でbest viable functionを決定する。もともとの統一関数呼び出し記法が、f(x.y)でまずフリー関数を優先し、存在しなければメンバー関数を探しに行くのと違い、フリー関数、メンバー関数のすべてが等しく比較される。

.x.f(y)は、.f(x,y)と同じ意味である。

unified call introducerは、チェインイングをサポートしている。例えば以下のように書くと、

.x.f(y).g.(z)

以下と同じ意味になる。

.g( .f(x,y), z )

何故か。まず最初の.x.f(y)は、先に述べたように.f(x,y)と同じ意味になる。これをexprとする。残りは、.expr.g.(z)なので、これの意味は.g( .expr, z )となる。exprは.f(x,y)だったので、最終的には.g( .f(x,y), z )と同じ意味になる。

もはやアボミネーションと言ってもいいぐらい醜悪な何かになっている。こんなわかりにくい提案は入ってほしくない。

P0302R1: Removing Allocator Support in std::function (rev 1)

std::functionからアロケーターを取り除く提案。

std::functionのコンストラクターにはアロケーターを引数に取るものがあるが、その意味については曖昧である。アロケーターをtype erasureで格納してあとからコピーのために元の型を取り出すのも煩わしい。既存の実装を見ると、libstdc++はそもそもアロケーターを取るコンストラクターを提供しておらず、libc++は、アロケーターを取るコンストラクターこそあるものの、アロケーターは無視され、MSVCの実装はアロケーターを使うものの、代入でtargetが書き換わった場合に再利用されないので、アロケーターの伝播ができない。

std::functionのアロケーターに対しては、様々な規格上の不備や矛盾が指摘されている。

そのため、std::functionからアロケーターサポートを取り除く。これはdeprecated扱いではなく。直ちに取り除く。

P0304R1: C++ Standard Library Issues Resolved Directly In Oulu

Oulu会議で採用された標準ライブラリに対する問題の解決案。

P0305R1: Selection statements with initializer

初期化子つき選択文

以下のようなコードを書くことはないだろうか。

auto obj = get_object() ;

if ( obj.is_valid() )
{
    obj.process() ;
}

一般に、変数を初期化し、初期化された変数の状態によって条件分岐し、分岐先で変数に対して操作を行う処理は多い。しかし、上記のコードには問題がある。まず変数を選択文の外で書かなければならず、選択文のあとにも変数は有効のままだ。変数の利用が選択文だけならば、変数の初期化を選択文に付属させたい。

この提案は、選択文の中で初期化子を使うことができる文法の追加だ。

if ( auto obj = get_object() ; obj.is_valid()
{
    obj.process() ;
}

この改訂版では、switch文に対しても、同様の文法が提案されている。

P0306R0: Comma omission and comma deletion

Cプリプロセッサーマクロ、__VA_ARGS__は、空の引数を受け取ることはできない。

#define F(...) f( __VA_ARGS___ )

// ill-formed
F( ) 

そこで、空の引数も受け付ける__VA_OPT__の提案。

#define F(...) f( __VA_OPT__ )

// f()
F()

[PDF] P0307R2: Making Optional Greater Equal Again

Optionalを再び偉大か同等に!と題された、どこぞの政治スローガンによく似たタイトルの文書。

現在提案されているoptionalは以下のように振る舞う。

optional<X> opt = ....;
assert(( opt >= opt) == true);
assert((*opt >= opt) == true);
assert(( opt >= *opt) == true);
assert((*opt >= *opt) == false);

理由は、optionalのoperator >=は、X型の!operator <を呼んでいるためだ。もしXの型が浮動小数点数だった場合、問題になる。

そういうわけで、正しい挙動は一番下の比較だけだ。いや、正しいというよりは、変えられない挙動というべきかもしれない。すると、一貫性のためには、他の3つの比較も全てfalseになるべきか。それも嫌だ。

この問題を修正するには、optionalのoperator >=が、保持する型のoperator >=を呼べばよい。なので、そのような修正を提案している。

P0308R0: Valueless Variants Considered Harmful

variantの要素型がすべてnoexceptなムーブコンストラクターを持つのであれば、variantはvalueless状態にならないことを保証すべきだという提案。

同様の保証を、std::listにも追加すべきとも提案している。

そして、結局valuelessになるかどうかはnoexceptなムーブコンストラクターがあるかどうかで決まるので、valuelessになるかどうかを判断する、valueless_by_exceptionはいらないのではないかとも提案している。

[PDF] P0309R0: Partial class

partial classの提案。

クラスのprivateなメンバーを変更したとき、そのクラスを使うソースファイルは全て再コンパイルしなければならない。たとえ、privateなメンバーを使わず、またクラスのオブジェクトも生成していないとしてもだ。これを防ぐには、virtual関数を使ったポリモーフィック型を使うか、pimplイディオムを使う必要がある。どちらもメモリ使用量、動的メモリ確保、ポインターのデリファレンスといった追加のコストが必要になる。

そこで、前方宣言を拡張して、partial classを追加する。partial classには、メモリレイアウトを変更しない宣言のみ書ける。

// stack_interface.hpp
partial class stack
{
public :
    void push(int x ) ;
    int pop( ) ;

    bool is_full() ;
    bool is_empty() ;
} ;

partial classは、後続するinternal classで定義できる。

class stack
{
pubic :
    void push( int x ) { buffer[++top] = x ; }
    int pop( ) { return buffer[--top] ; }
    bool is_full() { return top == max_size ; }
    bool is_empty() { top == -1 }

private :
    std::size_t max_size = 100 ;
    int buffer[max_size] ;
    int top = -1 ;
}

partial classは前方宣言と同じように不完全型となる。

partial classは、様々な別名をつけることができる。

partial class stack : export stack_interface1 { ... } ;
partial class stack : export stack_interface2 { ... } ;
partial class stack : export stack_interface3 { ... } ;

実装は別名を全部指定できる。

class stack : partial stack_interface1, partial stack_interface2, partial stack_interface 3 { }

やや筋が悪い提案のように思える。結局、partial classはポインターやリファレンス経由でしか使えないので、完全な定義がある翻訳単位からstaticストレージ、あるいは事前に一括して確保したストレージ上に構築したオブジェクトを、リファレンスかポインター経由でpartial classしか与えられていない翻訳単位に渡して使う形になる。

この提案の価値には疑問だったが、提案者がBMWに所属しているのでようやくわかった。極めて閉鎖的で不自由な組み込みプログラミング向けの機能だ。

組み込みプログラミングなので、メモリの動的確保はない。使うメモリは常に固定で確保している。また、ライブラリはヘッダーファイルとバイナリブロブで提供され、定義がソースコードで提供されることがない。これにより定義を書き換えても、再コンパイルが必要なくなる。ソースコードを見せる必要はない。

極めて筋の悪い提案だ。はっきりいってクソだ。車業界はクソだ。

ドワンゴ広告

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

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

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