C++1z 単一要素の波カッコ初期化をTに推論する

単一要素の波カッコ初期化(braced init list)を、(代入構文ではなく)直接autoで受けた場合のルールが、以下のように変更になります:

auto a {1};      // C++14まではinitializer_list<int>
                 // C++1zではint

auto b {1, 2};   // C++14まではinitializer_list<int>
                 // C++1zではコンパイルエラー

auto c = {1};    // これまで通りinitializer_list<int>
auto d = {1, 2}; // これもinitializer_list<int>

参照

お断り

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

C++1z if文とswitch文の条件式と初期化を分離

for文が for (初期化式; 条件式; 加算式)になっているのと同様に、if文とswitch文にも、条件式の前準備としての初期化式をif (初期化式; 条件式)のように書けるようになります。

// mapへの要素挿入
if (auto p = m.try_emplace(key, value); !p.second) {
  // 要素挿入に失敗 (すでにkeyが登録されている)
}
else {
  // 挿入した要素を使用して何か処理する
  process(p.first);
}
if (status_code c = open_file(); c != SUCCESS) {
  // 処理に失敗
  // 失敗の原因ごとに処理をする
  if (c == NO_FILE) {
  }
  if (c == FORMAT_ERROR) {
  }
}
switch (Button button(args); Status s = button.status()) {
  case OK: break;
  default: throw BadStatus(toString(s));
}

構文としては、

if (condition)
if (init-statement; condition)

のように2種類用意されるのではなく、初期化式を省略可能にして、以下のように定義されます。

if (init-statement;(opt) condition)

そのため、後に紹介するif constexpr文でも初期化式は使用できます。

参照

お断り

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

C++1z インライン変数

C++14まで、関数に対してインライン化の指定ができましたが、C++1zでは変数にもインライン指定ができるようになります。

変数に対してインライン指定をすると、翻訳単位を跨いでひとつのオブジェクトになります。これによって、ヘッダファイルで変数を定義できるようになります。

// foo.h
#ifndef FOO_HEADER
#define FOO_HEADER

struct Foo {};

struct X {
    // クラス定義の外でfooを定義する必要がない
    static inline Foo foo;
};

// これまでは、ヘッダファイルで以下のように変数を宣言し、
// foo.h
// struct X {
//   static Foo foo;
// };
//
// ソースファイルで変数を定義する必要があった
// foo.cpp
// Foo X::foo;

#endif
// foo.h
#ifndef FOO_HEADER
#define FOO_HEADER

struct Foo {};

// グローバル変数の定義をヘッダに書ける。
// 複数の翻訳単位を跨いでひとつのオブジェクトになる
inline Foo theFooObject;

#endif

なお、constexpr静的メンバ変数は自動的にインラインになりますので、明示的にinlineを指定する必要はありません。

参照

修正履歴

  • 2016/11/12 21:55 コメント欄での指摘を受け、「constexpr変数は自動的にインラインになる」としていた文章を、「constexpr静的メンバ変数は自動的にインラインになる」と修正。

お断り

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

C++1z 非推奨だったbool型に対するインクリメント演算子を削除

bool型に対してインクリメントすると固定でtrueになる仕様がありましたが、この仕様はC++98時点で非推奨になっていました。C++1zではこの機能が削除されます。

以下のようなコードは、C++1zではコンパイルが通らなくなりますので注意してください。

#include <cassert>

int main()
{
    bool b1 = false;
    bool b2 = true;

    // C++98から非推奨だがC++14まで合法、
    // C++1zではコンパイルエラー
    ++b1;
    b2++;

    assert(b1 == true);
    assert(b2 == true);
}

ちなみに、bool型に対するデクリメントは元々できません。

仕様としては、「算術型 (arithmetic type) に対してインクリメントできる」と書かれていたものが「(CV修飾された) bool型以外の算術型に対してインクリメントできる」に変わります。

参照

お断り

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

C++1z 非推奨だったregisterキーワードを削除

C++11から非推奨となっていたregisterキーワードが、C++1zで削除されます。

ただし、C++11のautoのように、registerキーワードを将来の標準でほかの用途に再利用することを考慮して、予約語としては残ります。

C++1zでは、register記憶クラス指定子を使用したプログラムは、コンパイルできなくなりますので注意してください。

参照

お断り

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

Elixirでコンソールに出力する文字色を変更して戻す

# 緑(green) + 太字(bright)で"hello"を出力し、元の文字色に戻す(reset)
IO.puts IO.ANSI.format([IO.ANSI.bright <> IO.ANSI.green, "hello", IO.ANSI.reset])

# 元の文字色で"world"が出力される
IO.puts "world"

リリースマネージャのDistillerymix releaseしたあとにコンソールの文字色が戻らないバグがあって、調べて直してpull requestを送ったので、そのときに書いたミニマムコードです。

C++1z 全ての非型テンプレート引数の定数式評価を許可

C++1zでは、非型テンプレート引数(non-type template argument)で扱える型はとくに変わりませんが、渡せる値についての制限緩和が行われます。

今回緩和されるのは、ポインタの値です。C++14までは、以下のような制限がありました:

  • 静的記憶域を持つ完全オブジェクトへのポインタ値もしくは参照、もしくは
  • ヌルポインタ値に評価される定数式、
  • ヌルメンバポインタ値に評価される定数式であること

つまり、staticでないオブジェクトへのポインタは、ヌルポインタしか渡せなかったのです。

C++1zではこの制限が撤廃され、定数式で評価されるポインタならなんでも渡せるようになります。その許可される定数式での評価には、配列からポインタへの変換や、関数から関数ポインタへの変換、修飾の変換なども含まれます。

struct A {};

template <const A* p>
struct X {};

constexpr A a{};
constexpr A ar[3] = {};

constexpr const A* get_pointer() { return &a; }
constexpr const A* get_array_pointer() { return ar; }

int main()
{
    X<nullptr> {};             // OK : これまで許可されていた
    X<get_pointer()> {};       // OK : C++1z
    X<get_array_pointer()> {}; // OK : C++1z
}

参照

お断り

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

C++1z 非型テンプレートパラメータのauto宣言

C++14まで、以下のように書いていた「指定された型の定数を受け取る」意図の非型テンプレートパラメータ(non-type template parameter)ですが、

template <class T, T V>
struct X;

X<int, 3>;

C++1zではこの用途のためのシンタックスシュガー(糖衣構文、syntactic sugar)が導入されます。そのためには、テンプレートパラメータをautoにして値を受け取るようにします。

template <auto X>
struct A {};

A<3>;    // OK
A<true>; // OK
A<'a'>;  // OK
A<3.14>; // コンパイルエラー (浮動小数点数は渡せない)

テンプレートの中では、decltypeを使用すればXの型を取得できます。

このautoは、変数宣言のautoと同じくプレースホルダーという扱いになります。そのため、template <auto* P>template <auto& R>のような推論補助もできます。

#include <type_traits>

template <auto* X>
struct A {
    using type = decltype(X);
};

int main()
{
    constexpr int* p = nullptr;
    static_assert(std::is_same<A<p>::type, int*>{});
}

参照

お断り

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

C++1z クラステンプレートのテンプレート引数推論

C++1zでは、クラステンプレートのテンプレート引数が推論されるようになります。

例として、冗長なコードになりがちなstd::lock_guardクラスを使用した以下のコードは、

std::mutex m1;
void f()
{
    std::lock_guard<std::mutex> lk(m);
}

C++1zでは以下のようにstd::lock_guardクラスのテンプレート引数を省略して書けるようになります:

std::mutex m;
void f()
{
    std::lock_guard lk(m);
}

この場合、mの型からstd::lock_guardクラスのテンプレート引数の型が推論されます。

C++1zでは、コンストラクタの引数からクラステンプレートのテンプレート引数が推論されます。std::lock_guardクラスの場合、先程のコンストラクタは以下のような宣言になっています:

template <class Mutex>
class lock_guard {
public:
    typedef Mutex mutex_type;
    explicit lock_guard(mutex_type& m);
};

このコンストラクタでは、その引数の型がそのままクラステンプレートのテンプレート引数になっているため、推論ができるようになっています。

ですが、変換コンストラクタのように、コンストラクタの引数から直接クラステンプレートのテンプレート引数を推論できない場合があります。イテレータ範囲を受け取るvectorのコンストラクタを考えます:

namespace std {
    // 簡略化したvectorとコンストラクタの宣言
    template <class T>
    class vector {
    public:
        template <class InputIter>
        vector(InputIter first, InputIter last);
    };
}

この場合は、引数の型InputIterからTを直接推論することはできません。このような場合には、ライブラリ側で推論の補助をする必要があります。推論補助は、クラステンプレートと同じスコープ (クラスの外)に、後置戻り値型 (trailing return type)と同じ構文で、コンストラクタに戻り値の型を明示します:

namespace std {
    template <class T>
    class vector {
    public:
        template <class InputIter>
        vector(InputIter first, InputIter last);
    };

    // 推論補助 (deduction-guide)
    template <class InputIter>
    vector(InputIter first, InputIter last)
        -> vector<typename iterator_traits<InputIter>::value_type>;
}

これでイテレータ範囲を受け取るvectorのコンストラクタでもクラステンプレートのテンプレート引数が推論できるようになり、以下のように書けるようになります:

std::list ls = {1, 2, 3};
std::vector v(ls.begin(), ls.end());

C++1z時点では、言語側の対応が入るのみで、標準ライブラリではクラステンプレートのテンプレート引数推論のための補助が入る予定はありません。

参照

お断り

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

編集履歴

  • 2016/10/25 12:47 : コメント欄でのk_satodaさんの指摘を受け、クラス内のコンストラクタに対して推論補助を記載していたところを、クラス外で推論補助するよう修正

C++1z 入れ子名前空間の定義

C++1zでは、ネストした(入れ子になった)名前空間の定義が簡潔に書ける構文が追加されます。

// C++14
namespace A { namespace B { namespace C {
}}}

// C++1z
namespace A::B::C {
}

入れ子名前空間の定義では、inlineや属性の指定はできません。

参照

お断り

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