本の虫

著者:江添亮
ブログ: http://cpplover.blogspot.jp/
メール: boostcpp@gmail.com
Twitter: https://twitter.com/EzoeRyou
GitHub: https://github.com/EzoeRyou

アマゾンの江添のほしい物リストを著者に送るとブログ記事のネタになる

筆者にブログのネタになる品物を直接送りたい場合、住所をメールで質問してください。

C++標準化委員会の文書: P0174R0-P0189R0

P0174R0: Deprecating Vestigial Library Parts in C++17

標準ライブラリの一部をdeprecated扱いとする提案。

以下のライブラリのdeprecated扱いが提案されている。

std::iteratorは基本クラスとして使い、イテレーターの要件を満たすためのネストされた型名をわざわざ手動で定義する手間を省いてくれる。

しかし、本当に手間を省けるかというと疑問だし、クラステンプレートから非修飾名として使うと、依存名ではない問題が発生するので、わかりにくい。たとえば、以下のコードがエラーになる理由は、とてもわかりにくい。

#include <iterator>

template <typename T>
struct MyIterator : std::iterator<std::random_access_iterator_tag, T> {
   value_type data;  // エラー、value_typeが見つからない 
};

maptとmultimapのvalue_compare。

mapとmultimapの実装には使えないし、ユーザーに使われている様子もない。

入力にイテレーターの片方しか取らない版のアルゴリズム(std::mismatchなど)は危険なのでdeprecated扱いにする。

std::allocatorの冗長なメンバー

allocator_traits経由で使うようになったので、冗長なメンバーはdeprecated扱いにする。

文書では、その他の候補としても、いくつかのライブラリを上げている。

vector<bool>は混乱のもとで廃止したいが、ストレージ効率のよい動的に確保されるストレージ上に構築されたビット列コンテナーの代替案が現在はない。

is_literal

is_literalには実用性がない。というのも、ジェネリックなコードで本当に知りたいのは、ある構築が定数初期化であるかどうかであって、型の構築方法の一つがリテラル型になるかどうかなどは実用性がない。

get_temporary_buffer/return_temporary_buffer

もともと不完全なままSTLに残っていたのがそのまま規格にも残ってしまった誰もまともに使っていないライブラリ。

raw storage iterator

今となってはあまり利用価値がない。

無駄な機能は積極的にそぎ落としてほしい。

P0175R0: Synopses for the C library

C++の規格にC言語のライブラリの宣言を追加する提案。今まではヘッダーファイルだけ参照していた。

ユーザーとしては特に変わることはない。

P0177R0: Cleaning up allocator_traits

allocator_traitsのpropagateがイマイチ理解できない理由がわかった。

propagateはアロケーターが状態を持っているかどうかを判別するのに使うのだが、コピー代入とムーブ代入とswapに対して状態があるかどうかを判定するようになっているので、2^3通りの組み合わせがある。当然、ライブラリ側も2^3通りの組み合わせに対処しなければならない。

しかし、現実的に考えれば、アロケーターは状態を持っているかいないかの2通りの場合しかないはずで、原稿の規格はおかしい。これを簡略化するために、既存のpropagate系の判定を全部deprecated扱いにして、単一のpropagateを追加する提案。

これは当然行われるべき変更だ。

P0178R0: Allocators and swap

コンテナーなどのアロケーターを使う型で、アロケーターが状態を持っていて、アロケーターのオブジェクトが等しくない場合、swapが未定義の場合について、それぞれ一時オブジェクトにムーブした上でムーブする方法を使うことで、未定義を回避するフリー関数swapの追加。

P0180R0: Reserve a New Library Namespace Future Standardization

コア言語に新しい機能が入り、ライブラリの設計方法に新しい手法が考案されていき、また現実の需要と事情が変化する中で、近い将来にC++の標準ライブラリには破壊的変更を伴う大幅な変更が必要になるだろう。そのために、新しい名前空間std2とisoを予約する提案。

死蔵されなければよいが。

Ordered By Default

setやmapは、要素の大小比較にoperator <を使っている。これは大抵の場合うまく行くが、世の中には数学的な大小比較を提供していない型も存在する。例えば、。std::complexだ。ただし、complexはsetやmapのための大小比較は提供できる。しかし、そのためには大小比較を定義した上で、比較子をコンテナーに渡してやらなければならない。

struct complex_order
{
    template < typename T >
    bool operator () ( std::complex<T> const & l, std::complex<T> const & r )
    {
        std::tie(l.real, l.img) < std::tie( r.real, r.img ) ;
    }
} ;

int main()
{
    std::set< std::complex<float>, complex_order > s ; 
}

いちいちコンテナに比較子を渡すのが面倒だ。lessを特殊化してやれば明示的に渡す必要はなくなる。

namespace std
{
    template < typename T >
    struct less< std::complex<T> >
    {
        bool operator () ( std::complex<T> const & l, std::complex<T> const & r )
        {
            std::tie(l.real, l.img) < std::tie( r.real, r.img ) ;
        }

    }
} ;

std::set< std::complex<float> > s ;

しかし、これではlessはoperator <を呼び出すものという既存のcontractがこわれてしまう。

そのため、ユーザーにカスタマイゼーションポイントとして、別のテンプレートを提供する。

template <typename T>
struct default_order {
   using type = std::less<T>;
};

template <typename T>
using default_order_t = typename default_order<T>::type;

template < typename Key, typename Compare = default_order_t<Key>,
            typename Allocator = allocator<Key> >
struct set ;

あとは、default_orderを特殊化するだけだ。

template < typename T >
struct default_order< std::complex<T> >
{
    using type = complex_order ;
} ;

この提案は、既存のコードのABI互換性を壊さない。

どんな問題でももう一段階のラッパーをかますことで解決できる。ただし、ラッパーが多すぎるという問題を除いては。

P0184R0: Generalizing the Range-Based For Loop

range-based for loopでイテレーターの先端と終端の型を別にできるように制限を緩和する提案。

Range TSは番兵という概念を導入している。番兵となるイテレーターは別の型でもよい。

struct range
{
    iter begin() const ;
    sentinel end() const ;
}

void f( range const & r )
{

    auto i = r.begin() ;
    auto e = r.end() ;

    for ( ; i != e ; ++i )
    {
        do_somethign( *i ) ;
    }
}

このようなrange型は、現在のrange-based forには渡せない。なぜならば、コード変換のルールが、イテレーターの先頭と終端の型が同じでなければならない制約になっているからだ。この制約をなくし、上のようなrangeをrange-based forに渡せるようにする変更。

適切な変更だ。

P0185R0: Adding [nothrow-]swappable traits (Revision 2)

std::is_swappable<T>, std::is_swappable_with<T, U>, std::is_nothrow_swappable<T>, and std::is_nothrow_swappable_with<T, U>と、その変数テンプレート版(名前が_tで終わるもの)をtype_traitsに追加する提案。

また、utilityにあるswapを、

void swap(optional<T>& rhs)
    noexcept(
        is_nothrow_move_constructible_v<T>
        && noexcept( swap(declval<T&>(), declval<T&>()))
    );

にする。

いい変更だ。

P0186R0: Iterator Facade -

イテレーターを簡単に書けるライブラリの追加。

標準ライブラリに準拠するイテレーターを正しく書くのはボイラープレートコードが多すぎて面倒だ。そこで、このライブラリはC++17とConcept TSとRange TSがあれば簡単にイテレーターが書けるライブラリを提案している。

Concept TSもRange TSもしばらく入らないことを考えると、このライブラリも当分日の目を見ないだろう。

[PDF] P0187R0: Proposal of Bit­field Default Member Initializers

ビットフィールド付きのデータメンバーにデフォルトメンバー初期化しを書けるようにする提案。

struct X
{
    int data : 6 = 42 ;
} ;

文法上の曖昧性を解消してパースを簡単にするために、初期化子には=しか使えない。そして、=の後には初期化子が続かなければならない。例えば{}は書けない。

まあ、いいのではないか。

[PDF] P0188R0: Wording for [[fallthrough]] attribute.

switch文のcaseラベルを通り抜けるのが意図的であると明示的に記述する[[fallthrough]]属性の文面案。

例えば以下のようなコードを書くと、

switch( n )
{
case 2 :
case 3 : // OK、特に何もない
    f() ;
    break ;

case 4 :
    g() ; // 警告、通り抜けてませんか?
case 5 :
    h() ;
}

最近のおせっかいなコンパイラーはcase 4でbreakを書き忘れていないかという警告をしてくる。もし、プログラマーの意図が、nが4の場合は関数gとhを両方呼び出し、nが5の場合は関数hのみ呼び出すというものであれば、上記のコードは正しい。そこで、プログラマーの意図をコード上で伝えるために、[[fallthrough]]を書くことができる。

case 4 : 
    g() ;
    [[fallthrough]]
case 5 :
    h() ;

いちいち書くのが煩わしい以外はいいか。

[PDF] P0189R0: Wording for [[nodiscard]] attribute.

関数の戻り値を使わないと警告される[[nodiscard]]属性の文面案。

// 失敗する可能性があるとても重要な処理
// 失敗した場合はfalseを返す
[[nodiscard]] bool very_important_operation() ;

int main()
{
    // 警告
    very_important_operation() ;
}

また、型に指定した場合、その型を戻り値として使うと、関数に[[nodiscard]]を指定した扱いになる。


[[nodiscard]] struct error_status { } ;
error_status f() ; // [[nodiscard]]がつく

これはC++17に入るコア言語機能の中でも最も一般に役に立つ機能だと思われる。

ドワンゴ広告

ドワンゴではとある一社員が個人的にお菓子神社という霊験あらたかな神社を開いており、参拝者も多い。筆者もポテトチップスという現世利益のために浄賽を投じようと思ったが、ポテトチップスは一袋335kcalもあり、ジョギング換算で5kmに相当し、脂肪換算で46gにあたるため、強い意志力を持ってやめておいた。

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

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

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