2016-06-21

C++標準化委員会の文書: P0220R0-P0229R0

P0220R1: Adopt Library Fundamentals V1 TS Components for C++17 (R1)

Library Fundamentals V1の規格への取り込み

Library Fundamentasについては、先日の勉強会資料でまとめている。

EzoeRyou/cpp17lib-slide: 勉強会のスライド資料

P0221R1: Proposed wording for default comparisons, revision 3

デフォルトの比較演算子を生成する提案。

デフォルトの比較演算子の挙動については、すでに解説してきた。現在問題になっているのは、スライシングだ。例えば以下の例をみてみよう。


struct B { int state ; } ;
struct D : B { int state ; }

void f()
{
    B b ;
    D d ;
    b < d ; // well-formed
}

b<dはwell-formedになる。なぜならば、オブジェクトdは、基本クラスBのサブオブジェクトへのリファレンスに、暗黙に変換されるからだ。つまり、上のコードは以下の意味を持つ。


b < static_cast< const B & >( d ) ;

この提案では、このスライシングを禁止する破壊的変更を伴う提案をしている。

提案はこうだ。コピーコンストラクター、operator =、比較関数、不等号関数に、クラスBから派生しているクラスDにたいして、const B &への暗黙の変換を禁止する。

その結果、上記のような合法なC++14コードはill-formedになる。

P0222R0: Allowing Anonymous Structs as Return Values

無名クラス型を関数の戻り値の型として返せる提案。

struct { int x, int y }
f()
{ return { 1, 2 } ; }

void g()
{
    auto [x, y] = f() ;
}

P0223R0: Class Namespace

クラスのメンバーをクラスの定義外で定義するときに、名前空間のようにクラススコープの中に入れることで、冗長な記述を省略できる文法の提案。

以下のようなクラスがあるとする

template < typename CharType, typename Traits, typename Allocator >
class MyString
{
    MyString( ) ;
} ;

クラスのメンバーをクラスの定義内で定義すると、クラス定義が極めて長くなってしまい。クラスの概要がつかみにくくなる。そのため、メンバーはクラスの定義外で定義したい。問題は、現状では極めて冗長な記述をしなければならない。

template < typename CharType, typename Traits, typename Allocator >
MyString< CharType, Traits, Allocator >::MyString( )
{
// ...
}

まずテンプレートを書いた上で、クラス名をテンプレートまで指定したスコープ解決演算子を書かなければならない。しかも、これをメンバー全てに対して記述する必要がある。

このような記述をするのは面倒だし、間違いのもとであるし、リファクタリングの妨げにもなる。そこで、以下のようにクラススコープを名前空間スコープのように使える文法を提案している。


template < typename CharType, typename Traits, typename Allocator >
namespace class MyString
{
    // 何も書かなくていい
    MyString()
    {
    // ...
    }
}

virtualやstaticはクラス名前空間では書けない。これは、現状でもクラス定義外では書けないからだ。

struct S
{
    virtual void f() ;
} ;

// virtualは書けない
void S::f() { }

P0224: Implicit Return Type

関数の戻り値の型を、先行する宣言から保管する提案。

int f() ;

// well-formed
// 戻り値の型はint
auto f() ;

C++14では、宣言が異なるためオーバーロード関数であるとみなされるが、引数が同じなのでill-formedになる。関数の戻り値の型でだけの違いではオーバーロード可能ではないので、不必要な制限である。

この制限緩和は、無名クラスを関数の戻り値の型として使えるようにするためである。


struct { int x, int y ; } f() ;

// OK
auto f()
{
    return { 1, 2 } ;
}

P0225R0: Why I want Concepts, and why I want them sooner rather than later

Ville Voutilainenによる、なぜConceptをC++17に入れるべきかという文書。

筆者の意見では、現状のConceptはC++17に入れるべきではない。

[PDF] P0226R1: Mathematical Special Functions for C++17, v5

数学関数をC++に追加する提案。

[PDF] P0227R0: Weakening the iterator categories of some standard algorithms

std::sort, std::inplace_merge, std::stable_sortのイテレーター要求をRandom Access IteratorからForward Iteratorに変更する提案。

アルゴリズムの研究の発展により、Forward Iteratorでもこれらのアルゴリズムをそれなりの効率で実装できるため。

[PDF] P0228R0: A Proposal to Add Safe Integer Types to the Standard Library Technical Report

数学的に正しく振る舞うかチェックできるライブラリ、std::safe<T>の提案

C++の組み込みの整数型は、数学の法則に正しく従わない。コンピューターの内部の挙動に従うように設計されている。これにより、以下のような数学的に正しいコードは、実際には正しく動作しない。

int f( int x )
{
    return x*x ;
}

なぜならば、x*xの結果は、int型で表現できる範囲を超えてしまうかもしれないからだ。

std::safe<T>は、整数型Tを置き換えて使うことができる。もし、結果が数学的に正しくない場合、例外が投げられる。

int f( std::safe<int> x )
{
    return x*x ;
}

ライブラリ実装により、実行時のオーバーヘッドを極力発生させないことができる。例えば以下のコード

int f( std::safe<std::int8_t> x )
{
    reutrn x*x ;
}

Integral promotionにより、x*xの結果の型はint型であり、それをint型として返す。int型はint8_t型同士の乗算結果を必ず数学的に正しく表現できるので、この場合に実行時チェックは必要ない。ライブラリの実装によって、そのような実行時チェックは省ける。

また、アーキテクチャによっては、演算結果が数学的に正しいかどうかをチェックできる機能を提供していることがあり、ライブラリがそのような機能を活用すれば、実行時のパフォーマンス向上に繋がる。

このライブラリは、整数型のDrop-in replacementとして使えることを目的としているため、追加のオプションなどはない。既存の似たようなライブラリには、オーバーフローやアンダーフローを検出するかどうかや、整数の範囲などを細かく指定できるものがある。このライブラリでは、単純性のため、そのような設計はしない。

なかなかいいライブラリだ。

[PDF] P0229R0: SG5 Transactional Memory Meeting minutes 2015/11/02-2016/02/08

トランザクショナルメモリの会議の議事録。短い。

ドワンゴ広告

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

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

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

1 comment:

Anonymous said...

タプルの返り値をスライスすることはそれなりにあるのでデフォルトで入るのはなかなか悪くないかもですね。