C++の提案 – の – 読み方



C++の提案 – の – 読み方

1 1


sugoi-goudou-2014

Slide for 凄い合同勉強会2014

On Github EzoeRyou / sugoi-goudou-2014

C++の提案

読み方

自己紹介

江添亮

株式会社ドワンゴ

C++標準化委員会 エキスパートメンバー

Blog: http://cpplover.blogspot.jp/ Mail: boostcpp@gmail.com Twitter: https://twitter.com/EzoeRyou GitHub: https://github.com/EzoeRyou

C++標準化委員会

ISOの下位組織

国家単位で活動組織が存在する

日本の場合、ITSCJ

文書

ISOの規程に基づき

C++標準化委員会の文書は

文書番号を付与して公開される

公開場所

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/

文書の内容

国際会議の案内、予定表、議事録

ドラフト文面、既知の問題集

問題点の考察

新機能の提案

新技術の紹介

具体的には?

注意

提案段階の文法や機能はまだ大幅に変わる可能性がある

そもそも採用されるかどうか分からない

実用できるのは10年は先の話

デフォルトの比較演算子

N4126

比較演算子を書ける?

struct X
{
    int a ;
    double b ;
    std::string c ;
} ;

等価、不等価

struct X
{

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

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

不等号

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

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

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

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

} ;

めんどくさい

ややこしい

間違えやすい

そもそも

各データメンバーが比較できるのであれば

機械的に生成できるではないか

デフォルトの比較演算子

比較演算子を自動生成

明示的に宣言することでデフォルト生成

互換性のために明示的に記述

既存のコードの意味を変えてはならない

提案文法

struct X
{
    int a, b, c ;

    bool operator == ( const X & ) = default ;
    bool operator != ( const X & ) = default ;

    bool operator < ( const X & ) = default ;
    bool operator > ( const X & ) = default ;
    bool operator <= ( const X & ) = default ;
    bool operator >= ( const X & ) = default ;
};

まだ使いづらい

省略された提案文法

struct X
{
    int a, b, c ;

    default: ==, !=, <, >, <=, >= ;
} ;

まだまだ面倒

暗黙のデフォルト生成

N4175

暗黙に生成することを提案

便利な条件演算子

N4120

"x ? x : y"を"x?:y"に

よくあるコード

xがtrueならばxを、falseならばyを返す

x ? x : y ;

xが重複している

間違えやすい

式が二度評価されるため非効率的

// 重い計算処理
int calculate_value() ;
int fallback_value = 100 ;
calculate_value() ? calculate_value() ? fallback_value ;

効率のいい書き方

面倒くさい

機械的に変換できる

コンパイラーにやらせればよい

auto && temp = calculate_value() ;
temp ? temp : fallback_value ;

提案文法

x ?: y ;

以下と同等

auto && temp = x ;
temp ? temp : y ;

参照チェック条件

N4127

よくあるコード

  • ポインターを変数に代入
  • nullかどうかをチェックする
  • nullでなければ処理を行う
if ( std::shared_ptr<T> ptr = obj.lock() )
{
    do_something( *ptr ) ;
}

シンタックスシュガー

もっと短く書きたい

if ( T & x : obj.lock() )
{
    f( x ) ;
}

展開後

if ( auto && __p = obj.lock() )
{
    T & x = * __p ;
    f( x ) ;
}

型まで省略したい

if ( x : e ) s ;

展開すると

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

インライン変数

もしくは、インライン式?

隠匿式とも

N4147

インライン変数

呼ばれるたびに初期化子が評価される変数

int runtime_data(
{
    static int count ;
    return ++count ;
}

inline int data = runtime_data() ;

int main()
{
    data ; // 1
    data ; // 2
    data ; // 3
}

用途

ストレージが確保されないことを保証

ODR回避

名前付き実引数

N4172

引数の多い関数はわかりにくい

void draw_rect( int left, int top, int width, int height )

int main()
{
    // どれがどれだっけ?
    draw_rect( 30, 30, 200, 300 ) ;
}

引数名を指定したい

int main()
{
    // わかりやすい
    draw_rect( left : 30, top : 30, width : 200, height : 300 ) ;

    // 順番も変えられる
    draw_rect( top : 0, left : 0, width : 640, height : 480 ) ;
}

ドット演算子

N4173

ドット演算子とは

operator . ()をユーザー定義したい

class string_ref
{
    std::string value ;
public :
    std::string & operator . () { return vlaue ; }
} ;

int main()
{
    string_ref ref ;

    // std::stringのlength
    ref.length() ;

    // ref.operator .() = "hello"
    ref = "hello" ;
}

使い道

スマートリファレンスを定義できる

template < typename T >
class Ref
{
    T * p ;
public :
    Ref( ) : p( new T() ) { }
    ~Ref() { delete p ; }

    T & operator . () { return *p ; }
} ;

上書き

こういうクラスがある場合

class string_ref
{
    std::string value ;
public :
    std::string && operator .() { return value ; }
    std::size_t size() ;
} ;

上書き

クラスにメンバーがある場合は優先される

int main()
{
    string_ref ref ;

    // string_refのsize
    ref.size() ;

    // std::stringのlength
    ref.length() ;
}

UTF-8文字リテラル

N4197

UTF-8文字リテラル

UTF-8コード単位ひとつで表現できる文字を記述できる

// OK
char c = u8'c' ;

// エラー
char あ = u8'あ' ;

名前空間のネスト

N4230

名前空間のネスト

深い名前空間のネストは面倒

namespace A { namespace B { namespace C {
} } } 

名前空間のネスト

簡潔に書けるようになる

namespace A::B::C {
}

統一関数呼び出し

N4165

N4174

提案

    x.f( y, z )

    f( x, y, z )

と同等にする

より汎用なコード

メンバー関数とフリー関数は呼び出しの文法が異なる

汎用的なコードの妨げになる

template < typename T >
void f( T & a, T & b  )
{
    // Tはクラスの場合
    a.swap( b ) ;

    // Tがクラスではない場合
    swap( a, b ) ;
}

最近の動向

コードの汎用性を上げるため

メンバー関数を使わないようにしよう

Fucntions Want to be free.

関数は自由になりたがっている。

-- Scott Meyers

解決案

関数呼び出しの文法を統一してしまえばよい

x.f(y) を f( x, y ) と同等にすればよい

ツールサポート

コード補完が強力になる

void f( std::FILE * fp )
{
    // コード補完可能
    fp->fseek
}

現実

move, swap, addressofなど

汎用的すぎる関数が大量にある

それほど便利にはならない

fp->for_each

コンテナーに不完全型

N4056

不完全型の要素

こう書けるようになる

struct Node
{
    // 現状では未定義
    std::vector<Node> node_list ;
} ;

理由

クラスは定義後に完全形になる

class Node ; // 宣言

Node n ; // エラー、不完全型
Node * p ; // OK

class Node { } ; // 定義
Node n ; // OK

実装可能性

コンテナーで不完全型に対応するのは可能

規格で規定されていない

実装ごとに対応はバラバラ

GCDとLCM

N4061

GCDとLCM

GCD - Greatest Common Divisor

最大公約数

LCM - Least Common Multiple

最小公倍数

標準ライブラリに提案

std::gcd( 10, 25 ) ; // 5
std::lcm( 123, 456 ) ; // 18696

現実的な提案

まだ採用されていないものの

かなり確実に採用される

すでにClang上流で実装済み

メッセージなしstatic_assert

N3928

static_assertの文法

必ず文字列リテラルが必要

static_assert( constant-expression , string-litera ) ; 

文法違反の例

// エラー
static_assert( expr ) ;

// OK
static_assert( expr, "" ) ;

文字列リテラルの省略を許可

Clangで実装済み

省略Range-based for

N3994

Range-based forは便利

for ( T elem : range )
    statememt ;

型名を記述するのが面倒

template < typename Container >
void f( Container & c )
{
    for ( Container::value_type & elem : c )
    {
        // ...
    }
}

autoが使えるが

正しく使うのが面倒

なぜこう書かねばならぬかは論文参照

for ( auto && elem : c )
{
    // ...
}

省略文法

auto &&と同じ意味になる

Clangに実装済み

for ( elem : range )
{
    // ...
}

テンプレート

テンプレートパラメーター

typename

N4051

文法上の制約

C++には不思議な文法上の制約がある

template <
    template < typename T >
    // classキーワードのみ使える
    class U >
struct X ;

typenameも許可

Clangで実装済み

template <
    template < typename T >
    // OK
    typename U >
struct X ;

ドワンゴ広告

このスライド資料はドワンゴ勤務中に書かれた

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

http://info.dwango.co.jp/recruit/