2015-06-09

C++標準化委員会: 2015-04 pre-Lenexaのレビュー: N4420-N4429

[PDF注意] N4420: Defining Test Code

C++の型システムにテストを追加する提案。

テストは重要である。C++標準はテストをサポートする機能を提供していない。そのため、C++プログラムはサードパーティのマクロ満載のお互いに非互換なテスト用フレームワークを使わなければならない。

この提案は、テストコードと非テストコードをプログラム中で明確に分断するための機能をテスト修飾子を提案している。

void f() test ; // テスト用コード

void g() test
{
    f() ; // OK
}

void h()
{
    f() ; // ill-formed
}

テスト修飾された関数やクラスは、非テスト修飾されたコードから使うことはできない。

N4421: C++ Standard Evolution Active Issues List

N4422: C++ Standard Evolution Completed Issues List

N4423: C++ Standard Evolution Closed Issues List

C++の新機能提案に対する既存の問題、解決済みの問題、却下された問題。

N4424: Inline Variables

inline変数の提案。

以下のようなコードを含むライブラリーがあるとする。

// library.h
struct X
{
    static int data ;
} ;

このライブラリーはヘッダーのみで使えることを想定した設計にしたい。さて、このstaticデータメンバーをどこかの翻訳単位で定義しなければならない。ではどこで定義すればいいのだろうか。

これを解決するには、inline関数がstaticストレージ上の変数へのリファレンスを返せばよい。

namespace detail {
    int & get_data()
    {
        static int data ;
        return data ;
    }
}

これは動くが、余計な関数をひとつ書かなければならないし、変数を使うときに冗長な関数呼び出し式を書かなければならない。

N4424提案は、変数をinline宣言することで、プログラム中で共通の実体を指すようにしてくれるものだ。

// library.h
struct X
{
    // クラス外に定義を書く必要はない
    static inline int data ;
} ;

以前提案されていたN4147は、inline変数というよりは、inline式とも言うべきもので、初期化子の式が、その副作用も含めて、変数を使った場所で評価されるというものだった。今回のN4424提案はどこかの翻訳単位に定義を書かずともプログラム中で共通の実体を指すという目的を限定したものになっている。

[PDF注意] N4425: Generalized Dynamic Assumptions

コンパイラーは最適化のために様々な情報を必要とするが、そのような情報は外部の環境に依存していて、コード中から取得できないことがある。もし、プログラマーがそのような乗法をコンパイラーへのヒントとして記述することができれば、コンパイラーはよりよく最適化ができる可能性がある。この提案は、そのような前提条件を記述できるコア言語機能を追加する提案である。

すでに、既存のC++コンパイラーで、コンパイラーに前提条件のヒントを与える独自拡張を提供しているものがある。

MSVCは__assume(expression)というintrinsicを提供している。ここに書かれた式はtrueとなるとみなされる。コンパイラーはこの情報を使って最適化ができる。式は評価されない。

IntelのC++コンパイラーも__assumeに加えて、__assume_alignedを提供している。これはポインターのアライメントの保証を記述するものである。

IBMのXLコンパイラーは__alignxというポインターのアライメント保証を指定するための機能を提供している。

Clangは__builtin_assumeと__builtin_assume_alignedというintrinsicを提供している。また、MSVC互換モードの場合、__assumeも受け付ける。

GCCは__builtin_unreachableと__builtin_assume_alignedを提供している。

提案されている文法は、既存のtrue/falseキーワードを再利用するものだ。

true(expression)は、オペランドの式がtrueと評価されることを保証する。false(expression)は、式がfalseと評価されることを保証する。式は実際には評価されない。


true( ++i ) ; // 式は実際には評価されないので、iはインクリメントされない。

true( i == 5 ) ; // コンパイラーはiは5と等しいとみなしてよい

true( false ) ; // この文には到達しない(コンパイラーはこの文を含むコードパスを除去してよい)

false( i < 2 ) ; // iは2未満にならない

void foo( int i )
{
    true( i > 6 ) ; // iは6より大きい保証

    // この分岐はコンパイル時に評価できるし、除去できる
    if ( i < 3 )
    {
        // ...
    }
}

また提案では、alingof演算子を拡張して、true/false演算子の中で使えるようにしている。

void bar( float * q, const float * p, int n, int m )
{
    true( alignof(p) == 16 && alignof(q) == 16 ) ;
    true( m % 16 == 0 ) ;


    // mは16の倍数であることが保証されているので、
    // コンパイラーは実行時のチェックなしに、
    // このループを余さず展開したりベクトル化したりできる
    for * int i = 0 ; i < n ; ++i )
    {
        q[i] = p[i] + p[i+m] ;
    }
}

契約プログラミングをより低級にした感じだ。

N4426: Adding [nothrow-]swappable traits

std::is_nothrow_swappable<T>を追加する提案。

加えて、この提案では、std::is_swappable<T>, std::is_swappable_with<T, U>, std::is_nothrow_swappable_with<T, U>も追加する。

[PDF注意] N4427: Agenda and Meeting Notice for WG21 Pre-Lenexa Telecon Meeting

電話会議の予定表

[PDF注意] N4428: Type Property Queries (rev 4)

静的リフレクション機能として、enum型とクラス型の情報を取得できるstd::enum_tratisとstd::class_traitsの提案。

enum_traitsは、テンプレートに渡したenum型の列挙子の識別子と値を取得できる。

利用例

template < typename T,  std::size_t I >
int print( )
{
    std::cout << std::enum_traits<T>::enumerators::identifier << '\n'
        << std::enum_traits<T>::enumerators::value << std::endl ;
    return 0 ;
} 

template < typename ... dummy >
void expand( dummy ... ) { } 


template < typename T, std::size_t ... I >
void print_enumerators_impl( std::index_sequence< I ... > )
{
    // 引数の評価順序を固定しようというN4228提案が可決されることを信じている
    expand( print< T,  I > ( ) ... ) ;
}

template < typename T,
    typename Indices = std::make_index_sequence< std::enum_traits<T>::enumerators::size > >
void print_enumerators( )
{
    print_enumerators_impl<T>( Indices() ) ;   
}

class_traitsについて詳しくは論文を参照してもらうとして、提案では基本クラスの型とvirtual基本クラスかどうか、メンバーの識別子とポインター、ネストされた形の識別子と型の一覧を取得できる。取得できるのはpublicなメンバーのみ。

このようなtraitsにしておけば、将来の拡張は容易いとしている。

また論文は、将来の拡張として、reflectidというキーワードを提案している。これは、reflectid(E)のように使う。オペランドに式を与えると、decltypeのように、式を評価した結果の型を使う。reflectid(E)のEがenum型の場合、結果はenum_traits<E>型になる。reflectid(C)のCがクラス型の場合、結果はclass_traits<C>になる。

reflectidが必要な理由は、アクセス指定に対応するためだ。reflectidが導入されれば、reflectidが使われた文脈に応じた情報を列挙したclass_traitsが得られる。また、名前空間はテンプレートパラメーターで渡す方法がないため、reflectidのようなコア言語でのサポートが必要だ。テンプレートもテンプレートに渡すよりは、reflectidが欲しい。

Clangベースの実験的実装が公開されている。

ChristianKaeser/clang-reflection

N4429: Rewording inheriting constructors (core issue 1941 et al)

継承コンストラクターの挙動を変更する提案。継承コンストラクターはusing宣言の文法を使うが、挙動が異なる。これまで、派生先でコンストラクターを生成して、基本クラスのコンストラクターにデリゲートするような定義をされていた。これにより、using宣言とは違った挙動が生じてしまう。

提案では、継承コンストラクターの挙動を、派生先クラスが基本クラスのコンストラクターを本当に継承したかのように定義する変更を提案している。これにより破壊的変更もあるが、挙動の不一致がなくなり、より自然になる。

具体的な例は論文を参照してもらうとして、様々な例が上げられている。

よい変更だと思う。

ドワンゴ広告

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

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

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

4 comments:

Anonymous said...

N4420のコンセプトが地味に好きです。
最適化に対するヒントですが、プログラマが優秀でコンパイラさんの判断をこえられるかというとちょっと疑問に思いますね。この評価がどういう風に展開されるか把握しないとちょっと怖いです。
enumを[文字列<->構文]したい需要は自分もあります。INFファイル書くときとか。なんかゼロコストで展開できる方法を探しているんですがなかなかうまくいきません。

Anonymous said...

>N4228
関数引数を並列処理で評価できなくするってことですか?

Anonymous said...

N4228を読みましたけど、引数の評価順とデータの依存性を混同していると思います。
参照の評価順を規定してないので簡単に破綻するし、並列化を捨ててまで導入する意味あるんでしょうか。

Anonymous said...

並列化できるとコンパイラが判断できるようなもんなら並列化するだろうし
できないようなもんならそもそも明示的に並列化するべきでは