C++と色々

主にC++やプログラムに関する記事を投稿します。

Visual C++ 2015 PreviewのC++11/14/1zの新機能

Visual C++ 12.0 CTPと比べて新しく対応したC++11/14/1zの新規の機能を紹介します。

コア言語

  • Terse range-based for

訳すなら簡便な範囲for文、って感じでしょうか? C++14の次の規格で提案されている機能で、正式に入ることは決定していませんが、ほぼ入ると思われます。
(2014/12/13追記 C++1zの機能から取り除かれたようです)
これは範囲for文の型宣言を省略した時に、auto&&と型を書いたように振る舞う機能です。

#include <iostream>
#include <vector>

int main()
{
    std::vector<int> v = {1, 2, 3, 4, 5};
    for (i : v) { // for (auto&& i : v)と同等になる
        std::cout << i << std::endl;
    }
}
  • 汎用ラムダキャプチャー

C++11の時の暫定的なラムダキャプチャーの仕様から、統一的な記法でラムダキャプチャーを書けるようになりました。

#include <memory>
#include <iostream>

int main()
{
    auto ptr = std::make_unique<int>();
    int a = 0;
    [p = std::move(ptr), &a = a] { std::cout << *p << a; }();
}

ラムダ導入子(lambda introducer)に左辺 = 右辺という風に書きます。左辺にはラムダの中で使う変数(実装的にはラムダ式で作られる関数オブジェクトのメンバ変数)の名前、右辺にはキャプチャするラムダの外の変数を書きます。複数キャプチャする場合はカンマで区切ります。参照で受け取る場合は左辺の変数名の左に&をつけます。左辺と右辺は別のスコープの別の変数のため同じ名前をつけても問題ありません。

C++14で2進数リテラルが入りました。

int main()
{
    static_assert(0b11 == 3, "");
}
  • スレッドローカル変数

スレッドごとに異なる変数を定義することができます。

#include <iostream>
#include <mutex>
#include <thread>
#include <vector>

namespace
{
    std::mutex mutex;
}

void test_thread_local(int n)
{
    static int sval = 0;
    thread_local int tval = 0;
    int val = 0;

    std::lock_guard<std::mutex> lock{mutex};
    sval += n;
    tval += n;
    val += n;

    std::cout << "static:" << sval
        << " thread_local:" << tval
        << " local:" << val << std::endl;
}

void f()
{
    for (int i = 0; i < 2; ++i) {
        test_thread_local(1);
    }
}

int main()
{
    std::vector<std::thread> threads;

    for (auto i = 0U; i < 3U; ++i) {
        threads.emplace_back(std::thread{f});
    }

    for (t : threads) {
        t.join();
    }
}

実行結果

static:1 thread_local:1 local:1
static:2 thread_local:2 local:1
static:3 thread_local:1 local:1
static:4 thread_local:2 local:1
static:5 thread_local:1 local:1
static:6 thread_local:2 local:1
  • ユーザ定義リテラル(と標準ライブラリの対応)

機能の詳しい説明は以下を参照

本の虫: ユーザー定義リテラルのすべて

#include <string>

constexpr unsigned long long int operator""_k(unsigned long long int value)
{
    return value * 1000;
}

int main()
{
    using namespace std::string_literals;
    "hoge"s.length();
    static_assert(1_k == 1000, "");
}
  • noexcept

Visual C++ 12.0 CTPでもnoexceptは入ったが、あちらは部分的な実装で、関数の例外指定(しかも 定数式が受け取れない)のみとなっている。こちらは完全な実装となっており、定数式も受け取れますしnoexcept演算子も使えます。

int main()
{
    static_assert(noexcept(1 + 1), "");
}
  • inline namespace

inline namespaceで宣言された名前にはその名前空間名を省略してアクセスすることが出来る。

inline namespace hoge
{
    int a;
}

int main()
{
    hoge::a;
    a;
}

char16_tchar32_t``と、リテラルのプレフィックスu8uU`が追加されました。

int main()
{
    char const* u8str = u8"aaa";
    char16_t const* u16str = u"bbb";
    char32_t const* u32str = U"ccc";
}

標準ライブラリ

  • u16stringu32stringstring_literals

前述のchar16_t、char32_tに対応するbasic_string方が定義されました。 対応するbasic_stringを得ることが出来るユーザ定義リテラルsも実装されました。

#include <string>

int main()
{
    char const* u8str = u8"aaa";
    char16_t const* u16str = u"bbb";
    char32_t const* u32str = U"ccc";
    std::string s1 = u8str;
    std::u16string s2 = u16str;
    std::u32string s3 = u32str;
    using namespace std::string_literals;
    "ddd"s.length();
    u8"eee"s.length();
}

前述の<string>の他にも<chrono><complex>がユーザ定義リテラルに対応しました。

C++14において、Forward Iteratorをデフォルト初期化した場合、どこも指さないnull iteratorになることが定められました。それに対応しました。参考

C++14 ヌル前方イテレータの規定 - Faith and Brave - C++で遊ぼう

  • quoted

<iomanip>にstd::quotedが追加されました。これはストリームに文字列を出力する際にダブルクオーテーションで囲む機能、ストリームから文字列を取得する際にダブルクオーテーションで過去まわれた文字列を1つの文字列としてダブルクオーテーションを除いて取得できる機能です。C++14です。

#include <iostream>
#include <iomanip>
#include <sstream>

int main()
{
    std::string s;
    std::istringstream iss{"\"hello world\""};
    iss >> std::quoted(s);
    std::cout << s << std::endl; // hello world
    std::cout << std::quoted("hello world") << std::endl; // "hello world"
}

C++14でstd::setstd::mapの以下のメンバ関数メンバ関数テンプレートが追加されました。

  • find()
  • count()
  • lower_bound()
  • upper_bound()
  • equal_range()

  • コンパイル時整数シーケンス

<utility>std::integer_sequenceとこれを生成するstd::make_integer_sequence、integer_sequenceのStd::size_tの特殊化であるstd::index_sequenceとこれを作成するstd::make_index_sequence、テンプレートパラメータに渡された方の数を得られるstd::index_sequence_forが追加されました。コンパイル時にインデックスを伴う操作によく使われ、tupleやコンパイル時array等の操作関数で良く使わます。

  • exchange

<utiliy>std::exchangeが追加されました。これは第1引数で受け取った変数を第2引数で受け取った値で代入し、古い値を返す関数です。1度しか状態が変化しない変数などが簡潔に書けるようになります。

  • 2つめの範囲を受け取るアルゴリズムに2つ目の範囲の終端を受け取る版を追加

タイトル通りですが、今までは2つの範囲が等しいサイズかみていませんでした。2つ目の範囲のほうが1つ目の範囲よりサイズが小さい場合、未定義の動作を起こしてしまいます。そこで2つ目の範囲の終端を受け取るオーバーロードが追加されました。

#include <algorithm>
#include <array>
#include <iostream>

int main()
{
    std::array<int, 3> a1 = {1, 2, 3};
    std::array<int, 4> a2 = {1, 2, 3, 4};
    std::cout << std::boolalpha;
    std::cout << std::equal(a1.begin(), a1.end(), a2.begin()); // true
    std::cout << std::equal(a1.begin(), a1.end(), a2.begin(), a2.end()); // false
}
  • get<T>tuple_element_t

C++11ではインデックスを指定して値を取得するget<std::size_t>がありましたが、新たに型を指定して値を取得するgetが追加されました。また、tuple_element<I, T>::typeエイリアスであるtuple_element_tも追加されました。

未実装の機能

パッと思いついたもので不完全です

  • constexpr(C++11/14)
  • Attribute
  • 変数テンプレート
  • initializer_listによるaggregate初期化の制限緩和
  • 標準ライブラリのconstexpr対応
  • 桁区切り
  • SFINAE式

疲れた……