C++1z swap可能かを判定する型特性クラス

swap操作をラップした関数のnoexcept条件を書きやすくすることを主な目的として、C++1zから以下の型特性クラスが追加されます。

  • Tswap可能かを判定するis_swappable<T>
  • T型Uの組み合わせでswap可能かを判定するis_swappable_with<T, U>
  • 例外を送出せずにswap可能かを判定するis_nothrow_swappable<T>is_nothrow_swappable_with<T, U>
  • この4つの変数テンプレート版

たとえば、これまで以下のように宣言されていた組み込み配列版のswap関数は、

// <utility>
namespace std {
  template <class T, size_t N>
  void swap(T (&a)[N], T (&b)[N]) noexcept(noexcept(swap(*a, *b)));
}

C++1zでは以下のような宣言になります。

// <utility>
namespace std {
  template <class T, size_t N>
  void swap(T (&a)[N], T (&b)[N]) noexcept(is_nothrow_swappable<T>::value);
}

宣言

// <type_traits>
namespace std {
  template <class T, class U> struct is_swappable_with;
  template <class T> struct is_swappable;

  template <class T, class U> struct is_nothrow_swappable_with;
  template <class T> struct is_nothrow_swappable;

  // 変数テンプレート版
  template <class T, class U>
  constexpr bool is_swappable_with_v
    = is_swappable_with<T, U>::value;
  template <class T>
  constexpr bool is_swappable_v
    = is_swappable<T>::value;

  template <class T, class U>
  constexpr bool is_nothrow_swappable_with_v
    = is_nothrow_swappable_with<T, U>::value;
  template <class T>
  constexpr bool is_nothrow_swappable_v
    = is_nothrow_swappable<T>::value;
}

参照

お断り

この記事の内容は、C++1zが正式リリースされる際には変更される可能性があります。正式リリース後には、C++日本語リファレンスサイトcpprefjpの以下の階層の下に解説ページを用意する予定です。

C++1z 現在発生している例外の数を取得するuncaught_exceptions()関数

C++98時点で、捕捉していない例外があるかどうかを判定するstd::uncaught_exception()関数が定義されていました。この関数は、例外が送出されてから捕捉されるまでの間でtrueを返します。

C++1zでは、捕捉していない例外がいくつあるかを返すstd::uncaught_exceptions()関数が定義されます。

// <exception>
namespace std {
  int uncaught_exceptions() noexcept;
}

この関数は、トランザクション処理で使用します。以下のように例外処理が入れ子になっている場合、

RAII1 raii1;
try {
    RAII2 raii2;
    try {
        <next>
    }
    catch (...) {
        <rollback>
        throw;
    }
}
catch (...) {
    <rollback>
    throw;
}

「予定していた例外の数よりも多くの例外が送出されていたら、処理の失敗とみなしてロールバック処理を行う」というようなことが必要となります。そのような状況でこの関数を使用します。

参照

お断り

この記事の内容は、C++1zが正式リリースされる際には変更される可能性があります。正式リリース後には、C++日本語リファレンスサイトcpprefjpの以下の階層の下に解説ページを用意する予定です。

「プログラマに必要なことを学ぶ会」を開催しました

数年前から、少人数でフリーテーマの勉強会をしたいと思っていました。私は普段、プログラミング関係の技術強化と、仕事全般で使用するような技術としての資質強化を交互にやってきていたのですが、資質強化の方はいろいろ身につけても話す場がなかったので、Boost.勉強会とはべつで新しい勉強会を始めることにしました。

少人数の勉強会にしたのは、参加者みんなの話を聞きたかったからです。Boost.勉強会は(私の意図とちがって)100人とかがポンと集まってしまうので、なかなか難しいです。

少人数の勉強会で課題となるのは、参加したいと思ってくれる潜在的な参加者のうち、本当に参加したいと強く思っている人が参加できるようにすることです。IT系の勉強会は一時期すごく流行って、いまだいぶ落ち着いてきましたが、それでも参加募集を開始すると一気に人が集まるところは多々目にします。そういった参加登録者のなかには「実際に行くかどうかはわからないけど、とりあえず席だけ取っておこう」という軽い気持ちで参加登録し、それによって本当に参加したいと思っている人が来れない、といった問題も起きていました。

そういった問題に対処するために、参加のハードルを少し上げることにしました。そのハードルの内容は参加募集のページを見ていただければわかります。結果的にすごくモチベーションのある人ばかりが集まり、白熱した議論が起こり、私の意図を十分満たす形で第一回を終えることができました。参加者層も、新卒の方、ゲーム系の方、システム・Web系の方、受託開発の方、請負開発の方、教職の方など、幅広いジャンルの人が集まっていました。

今回は、参加募集10人 + 運営者2人という人数で開催しましたが、2時間くらいの勉強会で、全員の話を十分に聞けるようにするには、これくらいの規模が上限のような気がしています。潜在的に参加したいと思ってくれている方が多くいるのは把握しているつもりですが、それを受け止めきれないのは、まだモヤモヤしているところです。

どれくらいこの勉強会が続くかはまだわからないですが、ひとまず第二回はなんとか開催できそうです。直近の私の課題としては、議論のスムーズな運営をするスキルをもうちょっと向上させたいですね。

Boost.勉強会 #20 東京をやります

2016年07月23日に、IIJさんの会場をお借りして、Boost.勉強会を開催します。

今回私は、C++1zについて話す予定です。
皆さまのご参加をお待ちしております。

C++1z atomic<T>::is_always_lock_free

C++11時点では、atomic<T>クラステンプレートの非静的メンバ関数としてis_lock_free()が用意されていました。この関数は、T型のatomicオブジェクトがロックフリーに振る舞うかどうかをbool値で返します。この関数が返す値はTの型ごとに静的な値ですが、atomicクラスのオブジェクトを作らないと、そのプロパティを参照できませんでした。

Tがロックフリーに振る舞えるかどうかによって、ユーザーが使用するアルゴリズムが変わる場合があり、そういった状況ではロックフリーに振る舞えるかどうかを静的に取得できるとSFINAEなどを使ってコンパイル時にアルゴリズムの選択ができて便利です。

<atomic>ヘッダで定義されるATOMIC_INT_LOCK_FREEのようなマクロで、型Tがロックフリーに振る舞えるかどうかを静的に取得できるので、それをラップして使いやすくした機能をC++1zで用意することになりました。

C++1zでは、atomicクラステンプレートに、is_always_lock_freeという名前の静的メンバ定数が定義されます。

namespace std {
    template <class T>
    class atomic {
    public:
        static constexpr bool is_always_lock_free = 実装定義;
    };
}

従来のis_lock_free()メンバ関数は、非推奨にはならないようです。

参照

お断り

この記事の内容は、C++1zが正式リリースされる際には変更される可能性があります。正式リリース後には、C++日本語リファレンスサイトcpprefjpの以下の階層の下に解説ページを用意する予定です。

C++1z コンテナに不完全型の最小サポートを追加

定義が完了していない不完全型(incomplete type)はsizeofがとれないので、アロケータとそれを使用するコンテナの実装が少し難しくなる場合があります。ですが、自分自身の型のオブジェクトがいくつあるかカウントする、というような場合に以下のようなコードを書くことになり、不完全型をコンテナの要素に指定できると便利です。

struct Entry {
    std::list<Entry> messages;
    // …
};

C++1zでは、以下の標準コンテナに限り、不完全型を要素とすることが許可されます:

  • std::vector
  • std::list
  • std::forward_list

ただし、これらのコンテナのなんらかのメンバ関数を呼び出す前には、要素型が完全型になっている必要があります。

なお、Boost Container Libraryでは1.61.0現在、static_vectorbasic_string以外の全てのコンテナに不完全型のサポートが入っています。(Unordered連想コンテナ、Multi-IndexといったBoost.Containerに含まれないBoostのコンテナは、その限りではありません)

参照

お断り

この記事の内容は、C++1zが正式リリースされる際には変更される可能性があります。正式リリース後には、C++日本語リファレンスサイトcpprefjpの以下の階層の下に解説ページを用意する予定です。

C++1z ファイルシステムライブラリ

C++1zでは、ファイルパス、ディレクトリ、ファイルのコピー・移動などを扱うファイルシステムライブラリが導入されます。

このライブラリは、Boost Filesystem Library v3をベースにしています。basic_path<CharT>のように文字型をパラメータとするのではなく、pathクラスが用意され、内部で文字エンコーディングが行われます。

C++1zのファイルシステムライブラリが議論されていくにつれてBoost.Filesystemも追従して更新されていますので、Boost 1.61.0現在では、この2つの差異は小さくなっています。

ファイルシステムライブラリの機能は、<filesystem>ヘッダのstd::filesystem名前空間で定義されます。

#include <filesystem>

namespace fs = std::filesystem;

int main()
{
    fs::path p1 = "C:/";
    fs::path p2 = "a.txt";

    // operator/()で2つのパスを連結する
    // operator+=(), concat()メンバ関数を使用してもよい
    fs::path p = p1 / p2;

    // ファイルをfromからtoにコピーする
    // エラーが発生した場合は、std::filesystem::filesystem_error例外が送出される
    fs::path from = p;
    fs::path to = "C:/b.txt";
    fs::copy_file(from, to);
}

エラー報告

ファイルシステムライブラリのエラー報告は、std::filesystem::filesystem_error例外を送出するか、std::error_codeオブジェクトを返すかのどちらかを、ユーザーが選択できます。

ファイルコピーの場合は、以下のようになります:

#include <iostream>
#include <filesystem>

namespace fs = std::filesystem;

int main()
{
    fs::path from = "C:/a.txt";
    fs::path to = "C:/b.txt";

    // デフォルトでは、エラー発生時に例外を送出する
    try {
        fs::copy_file(from, to);
    }
    catch (fs::filesystem_error& e) {
        std::cout << e.what() << std::endl;
        throw;
    }

    // 最後の引数にerror_codeオブジェクトへの参照を渡すと、
    // 例外送出の代わりにerror_codeオブジェクトにエラー情報が格納される
    std::error_code error;
    fs::copy_file(from, to, error);
    if (error) {
        std::cout << error.message() << std::endl;
    }
}

文字エンコーディング

std::filesystem::pathクラスは、文字コード関係の機能が内部に入っており、OSネイティブの文字コードでファイルパス文字列を取得したり、自分がほしい文字コードでファイルパス文字列を取得できたりします。

native()メンバ関数は、OSネイティブの文字コードでファイルパス文字列のstd::basic_stringを返します。文字の型は、POSIXベースのOSではcharWindowsではwchar_tになります。文字コードもOSネイティブのものになります。

// Windowsの場合
path p = "a.txt";
path::string_type str = p.native(); // string_typeはstd::wstring型
// POSIXベースOSの場合
path p = "a.txt";
path::string_type str = p.native(); // string_typeはstd::string型

文字コードを指定したい場合は、以下のメンバ関数を使用します。

メンバ関数 文字コード
std::string string() const OSネイティブのマルチバイ文字コード
std::wstring wstring() const OSネイティブのワイド文字コード
std::string u8string() const UTF-8
std::u16string u16string() const UTF-16
std::u32string u32string() const UTF-32

Unicodeの正規化に関する規定はとくにありません。

パスの汎用フォーマット

パスのフォーマットには、たとえばWindows"C:\\a.txt"ようにバックスラッシュでディレクトリを区切るものと、"C:/a.txt"のようにスラッシュで区切るものがあり、後者をこのライブラリでは汎用パスフォーマット(generic path format)と呼んでいます。

汎用フォーマットでパス文字列を取得したい場合に、以下のメンバ関数が使用できます。

template <class EcharT, class traits = char_traits<EcharT>,
          class Allocator = allocator<EcharT> >
basic_string<EcharT, traits, Allocator>
  generic_string(const Allocator& a = Allocator()) const;

std::string generic_string() const;
std::wstring generic_wstring() const;
std::string generic_u8string() const;
std::u16string generic_u16string() const;
std::u32string generic_u32string() const;

文字コードは、前述したものと同様です。

Boost.Filesystemとの差異

Boost 1.61.0のBoost.Filesystemライブラリと比較して、設計として以下の差異があります。

日時を表す型が、Boost.Filesystemライブラリではstd::time_tですが、標準の方はstd::chrono::time_point<trivially-clock>となっています。Clockの型は実装定義です。日時は、ファイルの最終日時を取得・設定する際に使用します。

ロケールの扱いが少し違います。Boost.Filesystemのpathクラスはimbue()メンバ関数を持っていますが、標準の方はありません。Boost.Filesystemのpathクラスはコンストラクタでcodecvtオブジェクトをとりますが、標準の方はlocaleオブジェクトをとります。 私が少し調べて気づいたのはこれくらいです。細かい機能のありなしはありますが、設計として異なるのはこれくらいでしょう。

参照

お断り

この記事の内容は、C++1zが正式リリースされる際には変更される可能性があります。正式リリース後には、C++日本語リファレンスサイトcpprefjpの以下の階層の下に解説ページを用意する予定です。

C++1z オブジェクトをconstにするas_const関数

C++1zから<utility>ヘッダに、as_const()関数テンプレートが追加されます。

この関数は、引数として渡されたオブジェクトをconstにします。その際、オブジェクトのコピーは発生しません。また、この関数に右辺値は渡せません。

#include <iostream>
#include <utility>

int main()
{
    std::string a = "hello";
    const std::string& b = std::as_const(a); // 非constの左辺値をconstにする
    const std::string& c = std::as_const(b); // const左辺値をそのまま返す
//  const std::string& d = std::as_const(std::move(a)); // コンパイルエラー
}

この関数は、constと非constオーバーロードがあった場合に、const版を意図して呼び出すことを目的としているのだそうです。

言語組み込みのconst_cast命令はconst (とvolatile)の付け外しができますが、こちらはconstを付けることしかできません。

宣言

// <utility>
namespace std {
    template <class T>
    constexpr add_const_t<T>& as_const(T& t) noexcept;

    template <class T>
    void as_const(const T&&) = delete;
}

参照

お断り

この記事の内容は、C++1zが正式リリースされる際には変更される可能性があります。正式リリース後には、C++日本語リファレンスサイトcpprefjpの以下の階層の下に解説ページを用意する予定です。

C++1z shared_from_thisの指す先が書き換わらないことを規定

thisポインタをstd::shared_ptrとして取得するための機能として、std::enable_shared_from_this基本クラスとそのメンバ関数shared_from_this()があります。

std::enable_shared_from_thisから派生したクラスのオブジェクトをnewしてshared_ptrコンストラクタに渡すと、そのオブジェクトのthisshared_ptrとして取得できるようになります。

しかし、以下のように同じポインタから複数shared_ptrオブジェクトを作った場合に、shared_from_this()で返されるshared_ptrがどのオブジェクトを指すのか規定されていませんでした。

#include <memory>

using namespace std;

int main()
{
  struct X : public enable_shared_from_this<X> { };
  auto xraw = new X;
  shared_ptr<X> xp1(xraw);  // #1
  {
    shared_ptr<X> xp2(xraw, [](void*) { });  // #2
  }
  xraw->shared_from_this();  // #3
}

実装によっては、 #3 がxp1を指す場合もあれば、xp2を指す場合もありました。xp2を指す場合は、shared_from_this()の再束縛が行われることを意味します。

C++1zでは、shared_from_this()が再束縛されないことが既定されます(この場合はxp1を指し続けます)。また、thisを束縛する動作がスレッドセーフではないことも明記されます。

元々未規定だった動作ですが、xp2を指すことを期待していたプログラムは動作しなくなる可能性があるので注意してください。

また、再束縛されない仕様を明確にすることを主な目的として、std::enable_shared_from_thisクラスに、weak_ptrthisを返すweak_from_this()メンバ関数が追加されます。

参照

お断り

この記事の内容は、C++1zが正式リリースされる際には変更される可能性があります。正式リリース後には、C++日本語リファレンスサイトcpprefjpの以下の階層の下に解説ページを用意する予定です。

C++1z reference_wrapperがTriviallyCopyableであることを保証

C++11では、関数テンプレートに渡す引数を参照として渡すことを明示するstd::reference_wrapperクラスが導入されました。

これは通常、オブジェクトへの参照を受け取ってそのポインタを保持しておく、という簡単な方法で実装されます。このような実装になっているとreference_wrapperオブジェクトに対してmemcpy操作ができるのですが、そうなっていない実装が存在した(MSVC)ということで、実装を強制する保証が入ります。

標準C++にはTriviallyCopyableというコンセプトがあり、reference_wrapperクラスのオブジェクトはテンプレートパラメータに何が指定されたとしても、TriviallyCopyableコンセプトの要件を満たすことが実装に求められるようになります。

この保証があることで、Small Buffer Optimicationという最適化を、reference_wrapperのオブジェクトに対しても適用できるようになります。

template <class T, class Buffer>
struct use_small_buffer_optimization
    : std::integral_constant<bool,
        sizeof(T) <= sizeof(Buffer)
     && alignof(Buffer) % alignof(T) == 0
     && std::is_trivially_copyable<T>::value
     && std::is_copy_constructible<T>::value
> {};

参照

お断り

この記事の内容は、C++1zが正式リリースされる際には変更される可能性があります。正式リリース後には、C++日本語リファレンスサイトcpprefjpの以下の階層の下に解説ページを用意する予定です。