2016-09-06

C++標準化委員会の文書: P0380R0-P0389R0

[PDF] P0380R0: A Contract Design

contract programmingの議論のたたき台になった実験的実装の紹介。属性でcontractsを指定する。

void push(queue & q)
    [[ expects: !q. full () ]] // there must be room for another element
    [[ ensures: !q.empty() ]] // q can’t be empty after adding an element
{
    // ...
    [[ assert: q.is_ok() ]]; // q’s invariant is (re)established at this point
}

contractsには、3段階の契約履行レベルを指定できる。defaultとauditとaxiomだ。defaultは最小の実行時コスト、auditは大きな実行時コストのかかる契約、axiomは人間のためのソースコード内ドキュメントと静的解析ツールのためのコンパイル時チェックのレベル


[[ expects: ...]] // 暗黙のデフォルト
[[ expects default : ... ]] //明示的なデフォルト
[[ expects audit : ... ]] // audit
[[ expects axiom : ... ]] // axiom

契約違反はプログラムの終了になる。契約違反の際の挙動も指定できる。無効、デフォルト、audit(デフォルト+audit契約チェック)

関数の宣言時に契約を記述した場合、すべての同じ関数の宣言は同一の契約を記述しなければならない。

まあ、言ってみればコア言語による高級assertだ。

P0381R0: Numeric Width

<cstdint>のジェネリック実装ライブラリの提案

<cstdint>には、int16_tやuint16_tなどといった、符号とビット長を指定した整数型があるが、この実装はジェネリックではない。ジェネリックコードから符号やビット長を指定したい。

そのために、width<T>とset_width<T, N>を提案する。

width_v<std::int16_t> ; // 16
width_v< char > ; // 少なくとも8以上
width_v< wchar_t > ; // 少なくともワイド文字以上
width_v< long long int > ; // 少なくとも64以上

set_widthは、指定した型と同じ符号で、指定したビット長以上の整数型を返す。

set_width_t< int, 8 > a ; // int8_t相当
set_width_t< unsigned, 32> b ; // uint32_t相当
set_width_t< char, 32 > c ; // 符号付きか符号なしの32bit長の整数
set_width_t< signed, 10 > d ; // 大抵の実装ではint16_t相当

charの符号は未規定なので、cの符号がどうなるかは実装に依存する。

set_widthは使いづらいように見えるが、テンプレート実引数として渡された型と互換性のある符号でビット長保証のある型を得るための設計となっている。

P0382R0: Comments on P0119: Overload sets as function arguments

P0119R1でテンプレート内で関数名からオーバーロード解決するために、関数名からクロージャーオブジェクトを合成する気嚢が定庵されているが、この機能は既存のコードにも将来のコードにも悪影響を及ぼすという反論。

確かにそうだ。

P0384R0: Core "tentatively ready" Issues

コア言語に入る予定の修正案

[PDF] P0385R0: Static reflection: Rationale, design and evolution

静的リフレクションについて、これまでの議論の経緯や推移を解説する82ページもある文書。

[PDF] P0386R2: Inline Variables

inline変数の提案。C++17のドラフトに入っている。

inline変数は、複数の翻訳単位で定義できる。定義は同一でなければならない。inline変数は、あたかもひとつの変数のオブジェクトがあるかのように振る舞う。

inline int x ;

extern宣言して、変数定義用の翻訳単位を用意する必要がなくなる。

constexpr staticデータメンバーは暗黙にinlineとなる。名前空間スコープのconstexpr変数は暗黙にinlineにはならない。

P0387R0: P0387R0: Memory Model Issues for Concurrent Data Structures

現在、Concurrent queueやdistributed counterといった、並列データ構造の提案が上がっている。並列データ構造のメモリーモデルはどうすればいいのか。

  1. memory_orderを引数に渡して指定する
  2. まともなコードでは観測不可能にする
  3. 非決定性であると規定する

1.について、並列データ構造をロックフリーにすることにパフォーマンス上の意味がないのであれば、memory_orderを指定する必要はない。2.について、現在、非現実的なコードで無理矢理にメモリーモデルの違いを観測することができるが、規格はそのような非現実的なコードについてはまともに取り合っていない。3.について、現在、規格はtry_lockを非決定性であるとしている。

P0388R0: Proposal: conversions to arrays of unknown bound

Core issue 393を解決する上で、関数の引数は要素数が未束縛の配列へのリファレンス型を使えるようになった。

void f( int (&)[] ) ;

問題は、要素数が束縛された配列型を、要素数が未束縛の配列へのリファレンス型で束縛することができないということだ。


int main()
{
    int a[1] ;

    int (&ref)[] = a ; // エラー
    f( a ) ; // エラー
}

このような制限は不要であるから緩和する提案。

現在、Clangは実装している。GCCはまだ実装していない。そのため、互換性の問題もない。

P0389R0: Proposal: template keyword in unqualified-ids

unqualifed-idがtemplate-idであることを示すためのtemplateキーワードを記述できるようにする提案。

メンバーテンプレートがテンプレートであることを示すために、templateキーワードが使える。

template < typename T >
void f()
{
    T::f<int>() ; // エラー、fはtemplate-idとはみなされない。<は演算子

    T::template f<int>() ; // OK、fはtemplate-idとみなす
}

同様のことを、名前空間スコープ内のtemplate-idに対して行いたい。そして、ADLによってname lookupされるようにしたい。

namespace N {
 struct A { };
 template <typename T>
 T func(const A&) { return T(); }
}

void f() {
 N::A a;
 func<int>(a); // エラー、funcはtemplate-idではない。
}

このコードがill-formedである直接の理由は、名前funcが見つからないためではない。ADLによって名前funcは見つかるのだが、template-idであるとみなされず、<は大小比較演算子であるとみなされてしまう。

N::func<int>と書くと、この問題は解決するのだが、それでは意味が変わってしまう。修飾名なのでADLが働かない。

そこで、非修飾名に対してtemplate-idであることを示すtemplateキーワードを記述できるようにする。その結果、以下のように書ける。

template func<int>(a) ;

一部のライブラリ作者が幸せになる機能だ。

ドワンゴ広告

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

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

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

2 comments:

Anonymous said...

契約がよさそうです。とある関数を持っているか。というのは契約の範囲なのでしょうか。テンプレート使っててたまに使いたくなるのでなんかの機能で入ってほしいです。

江添亮 said...

それは契約ではなくてコンセプトで提供される機能。