2014-10-15

2014-10-pre-Urbana mailingsのレビュー: N4122-N4129

2014-10-pre-Urbana mailingsが公開された。

N4122: AGENDA, PL22.16 Meeting No. 64, WG21 Meeting No. 59, November 3-8, 2014 – Urbana-Champaign, IL

2014年11月3-8日にUrbana-Champaignで開かれる会議の予定表。

N4123: Improvements to the Concurrency Technical Specification

Concurrency TSドラフトの変更提案。すでに提案されていたexperimentalヘッダーなどの細かい変更に対応するため。

N4124: Toward More Expressive Iterator Tags

イテレーターカテゴリーを表現するライブラリであるイテレータータグの定義を変更する提案。

イテレーターカテゴリーの実際の内容を個別に、reference_tagとかlvalue_tagなどと細分化し、イテレータータグはこれらの個別のタグの組み合わせで表現する。

現在のイテレータータグは、以下のように定義されている。

struct input_iterator_tag { };
struct output_iterator_tag { };
struct forward_iterator_tag: public input_iterator_tag { };
struct bidirectional_iterator_tag: public forward_iterator_tag { };
struct random_access_iterator_tag: public bidirectional_iterator_tag { };

タグというのは、異なる型として認識さえされればいいので、この定義なのだが、これではややわかりにくい。さらに、最近提案されているイテレーターは、ランダムアクセスイテレーターにみえて、実は既存のランダムアクセスイテレーターの定義をすべて満たしていないという問題が指摘されている。

このため、イテレーターができることを細分化したタグを定義し、既存のイテレータータグを、そのタグの組み合わせとして定義するという提案。

これを使い、既存のイテレータータグを、以下のように、組み合わせて定義する。

typedef packer<reference_tag,
               lvalue_tag,
               rvalue_tag,
               equality_comparable_tag,
               multipass_tag>
          forward_iterator_tag;

前回のN4068からの変更は、タグを組み合わせるために、N4115で提案されている汎用のコンパイル時限定tupleもどきであるpackerをつかうことになった。

これにより、例えば、あるイテレータータグの特性をコンパイル時に判定できるようになった。

template < typename Iterator >
void f( Iterator first, Iterator last )
{
    // Iteratorがプロクシーイテレーターかどうか判定
    constexpr bool b =
        std::contains_type_v<
            std::iterator_traits<Iterator>::iterator_category,
            std::packer< std::reference_tag > > ;
}

[PDF注意] N4125: 2014-09 WG21/SG1 Meeting Information

なぜか素手の終わった2014年夏の標準化委員会の会議の会場案内。CppConの前の週に行うそうで、CppConの会場も近い。

N4126: Explicitly defaulted comparison operators

デフォルトのメンバーごとの比較演算子を明示的に自動生成させようという提案。

クラスの比較が、クラスのメンバーごとの比較に落とし込め、クラスのメンバーは比較演算子をサポートしているとき、以下のようなコードを書かなければならない。

struct X
{
    int a, b, c ;

    bool operator ==( X & rhs ) const noexcept
    {
        return
            a == rhs.a &&
            b == rhs.b &&
            c == rhs.c ;
    }

    bool operator != ( X & rhs ) const noexcept
    {
        return !( *this == rhs ) ;
    }

    bool operator < ( X & rhs ) const noexcept
    {
        return std::tie( a, b, c ) < std::tie( rhs.a, rhs.b, rhs.c ) ;
    }

    bool operator > ( X & rhs ) const noexcept
    {
        return rhs < *this ;
    }

    bool operator <= ( X & rhs ) const noexcept
    {
        return !( rhs < *this ) ;
    }

    bool operator >= ( X & rhs ) const noexcept
    {
        return !( *this < rhs ) ;
    }

} ;

これは極めて面倒だ。これを間違えずに書くのは至難の業である。しかもこれはstd::tieを使っている。本当のメンバーごとのlexicographical comparisonは正しく書くのがひたすら苦行でめんどくさい上に、手で書くと間違えやすい。

そして、このコードは、もしメンバーの追加や削除が行われた場合、手で書き換えなければならない。極めて間違いやすい非人間的なコードであり、このコードを手で書くのは深刻な人権侵害である。こういうコードはよろしくコンパイラーが自動生成すべきなのだ。

互換性の問題もあるので、暗黙に比較演算子を生成させるわけには行かない。そこで、明示的な宣言によって生成させる文法を提案している。

struct Thing
{
    int a, b, c;
    std::string d;
};

// 明示的な自動生成
bool operator==(const Thing &, const Thing &)= default;
bool operator!=(const Thing &, const Thing &)= default;

メンバーがprivateな場合、演算子はfrined宣言しなkればならない。

class AnotherThing
{
    int a, b;

public:
    // ...

    friend bool operator<(Thing, Thing) = default;
    friend bool operator>(Thing, Thing) = default;
    friend bool operator<=(Thing, Thing) = default;
    friend bool operator>=(Thing, Thing) = default;
};

なるほど、確かに中身を書く必要はなくなったが、まだ冗長で面倒だ。そこで、もっと簡潔な文法も提案されている。

struct Thing
{
    int a, b, c;
    std::string d;

    default: ==, !=, <, >, <=, >=;   // defines the six non-member functions
};

前回の論文N3950からの変更点としては、論文で議論が必要だとされていた部分が会議によってコンセンサスが得られたところだ。ポインターや浮動小数点数やenum型はそのまま比較される。mutableメンバーが存在する場合は明示的な演算子が使用できない。簡潔な文法は会議で強力に支持された。

論文では、大小比較演算子の実装方法を二つ示している。ひとつはメンバーのstrict weak orderを使うもの、つまり大小比較演算子のみ使う。


bool operator<(const thing &t1, const thing &t2) 
{
    if (t1.a < t2.a) return true;
    if (t2.a < t1.a) return false;
    if (t1.b < t2.b) return true;
    if (t2.b < t1.b) return false;

    return t1.c < t2.c;
}

これは既存のpairやtupleも採用している比較方法である。

もうひとつは、メンバーのtotal orderを利用するもので、これは等号演算子の存在が必要になる。

bool operator<(const thing &t1, const thing &t2) 
{
    if (t1.a != t2.a)
        return t1.a < t2.a;
    if (t1.b != t2.b)
        return t1.b < t2.b;
    
    return t1.c < t2.c;
}

これは是非とも採用されて欲しい。

N4127: Checked-dereference conditions


if ( T x : e ) s

if ( auto && __p = e )
{
    T x = *__p ;
    s
}

に変換する文法の提案。

ポインターや、ポインター風の型の値をbool型に変換してfalseではないことを確かめてから、operator *()でデリファレンスして使うというコードは頻出する。そのようなコードを書くための短い文法だ。Range-based forの文法から着想を得ているらしい。

具体的な利用方法としては、例えば


void inc( int * ptr )
{
    if ( int & ref : ptr )
        ++ref ;
}

とか、

void inc( std::weak_ptr<int> wp )
{
    if ( T & ref = wp.lock() )
        ++ref ;
}

などのように使える。他には、提案中のoptionalのようなライブラリも利用例も考えられる。

この文法は、forやwhileでも使える。switchでは使えない。

また、「次世代」版として、型を省いてデフォルトでauot &&にする文法も提案されている。つまり、


if ( x : e ) s

if ( auto && __p = e )
{
    auto && x = *__p ;
    s
}

になる。

N4128: Ranges for the Standard Library, Revision 1

標準ライブラリにRangeの概念を導入した場合の設計の叩き台。

様々な設計上の考察が書かれている。

[PDF大注意] N4129: Source-Code Information Capture

ソースファイルの記述場所の行番号、行頭からの文字数、ファイル先頭からの文字数、ファイル名、関数名を取得するライブラリの提案。

source_context型のオブジェクトを構築した場所の情報が取得できる。

void f()
{
    std::source_context sc ; // この場所のソースファイル情報

    std::cout
        << "file name: " << sc.file_name() << "\n" <<
        << "line number" << sc.line_number() << "\n" ;

}

プリプロセッサーにたよらずに、ソースファイルの情報を取得できるようになる。いわゆるprintfデバッグ用やログの出力などに利用できる。

ドワンゴ広告

この記事はドワンゴ勤務中に書かれた。

ドワンゴでは、デリランチと称する新しい重量課金の昼食システムにより、昼職が劇的に改善されている。サラダが安い。

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

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

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

1 comment:

Anonymous said...

イテレータに関しては、誰かが明示的に日本語の説明を書いてくれないと使い勝手悪いですね。細分化そのものは時代かなーと思いますけど、利用者にちゃんと普及してほしい。
比較オペレータについては、さすがに自分の手で書くのは骨が折れるので提案自体は歓迎しますが、表記の手抜きはどうにかなりませんでしたかね。なんかここだけ妙に新しい。
チェックドデリファレンスコンディションズは、自分はまぁ賛成ですかねぇ。ぶら下がりで構文書くのはちと汚いと思っていました。
手抜き表記はあまり好きではないのですが、えーっとチェック忘れをなくす方が大事なのでこれは妥協しておきます。
その他については自分には関係なさそうです。またおいおい。