POSTD PRODUCED BY NIJIBOX

POSTD PRODUCED BY NIJIBOX

ニジボックスが運営する
エンジニアに向けた
キュレーションメディア

POSTD PRODUCED BY NIJIBOX

POSTD PRODUCED BY NIJIBOX

ニジボックスが運営する
エンジニアに向けた
キュレーションメディア

FeedlyRSSTwitterFacebook

本記事は、原著者の許諾のもとに翻訳・掲載しております。

(注:2017/10/25、いただいたフィードバックを元に翻訳を修正いたしました。修正内容については、 こちら を参照ください。)
(注:2017/02/07、タグが誤っていたので修正いたしました。)

C++11/14/17

概要

C++11/14/17に関する記述や例の多くは、様々なリソース(詳しくは謝辞の項目をご覧ください)を参考にしており、それらを自分の言葉でまとめてみました。

C++17には、以下の新しい言語機能が導入されています。

C++17には、以下の新しいライブラリ機能が導入されています。

C++14には、以下の新しい言語機能が導入されています。

C++14には、以下の新しいライブラリ機能が導入されています。

C++11には、以下の新しい言語機能が導入されています。

C++11には、以下の新しいライブラリ機能が導入されています。

C++17の言語機能

クラステンプレートに対するテンプレート引数の推論

これは、テンプレート引数の推論を自動的に行うもので、関数に対して実行される方法に似ています。今回、これにクラスのコンストラクタが導入されました。

template <typename T = float>
struct MyContainer {
  T val;
  MyContainer() : val() {}
  MyContainer(T val) : val(val) {}
  // ...
};
MyContainer c1{ 1 }; // OK MyContainer<int>
MyContainer c2; // OK MyContainer<float>

autoによる非型テンプレートパラメータの宣言

許可された型[*]の非型テンプレートパラメータリストを順守しながら auto の推論規則に従うと、テンプレート引数をその引数の型から推測することができます。

// Explicitly pass type `int` as template argument.
auto seq = std::integer_sequence<int, 0, 1, 2>();
// Type is deduced to be `int`.
auto seq2 = my_integer_sequence<0, 1, 2>();
  • ➖ 例えば、 double をテンプレートパラメータの型として使用できないだけでなく、 auto を使ってこれを無効の推論としてしまいます。

フォールド式

フォールド式は、二項演算子に対してテンプレートパラメータパックの折りたたみを行います。

  • op がフォールド演算子であり、 e が未展開のパラメータパックである (... op e) または (e op ...) の式を 単項フォールド と呼ぶ。
  • op1 op2 がフォールド演算子である (e1 op1 ... op2 e2) の式を 二項フォールド と呼ぶ。 e1 もしくは e2 のどちらかは未展開のパラメータパックでなくてはならないが、両方未展開である必要はない。
template<typename... Args>
bool logicalAnd(Args... args) {
    // Binary folding.
    return (true && ... && args);
}
bool b = true;
bool& b2 = b;
logicalAnd(b, b2, true); // == true
template<typename... Args>
auto sum(Args... args) {
    // Unary folding.
    return (... + args);
}
sum(1.0, 2.0f, 3); // == 6.0

かっこ付き初期化リストを持つauto推論の新しい規則

統一的な初期化構文と共に使用された場合、 auto 推論に変更します。以前は、 auto x{ 3 }; std::initializer_list<int> を推論していましたが、今回から int を推論するようになります。

auto x1{ 1, 2, 3 }; // error: not a single element
auto x2 = { 1, 2, 3 }; // decltype(x2) is std::initializer_list<int>
auto x3{ 3 }; // decltype(x3) is int
auto x4{ 3.0 }; // decltype(x4) is double

constexprラムダ

constexpr を使用したコンパイル時ラムダです。

auto identity = [] (int n) constexpr { return n; };
static_assert(identity(123) == 123);
constexpr auto add = [] (int x, int y) {
  auto L = [=] { return x; };
  auto R = [=] { return y; };
  return [=] { return L() + R(); };
};

static_assert(add(1, 2)() == 3);
constexpr int addOne(int n) {
  return [n] { return n + 1; }();
}

static_assert(addOne(1) == 2);

インライン変数

インラインの指定子は変数や関数に適用することができます。インライン宣言された関数は、インライン宣言された関数と同じセマンティクスを持ちます。

// Disassembly example using compiler explorer.
struct S { int x; };
inline S x1 = S{321}; // mov esi, dword ptr [x1]
                      // x1: .long 321

S x2 = S{123};        // mov eax, dword ptr [.L_ZZ4mainE2x2]
                      // mov dword ptr [rbp - 8], eax
                      // .L_ZZ4mainE2x2: .long 123

入れ子になった名前空間

入れ子になった名前空間の定義を作成するために、名前空間のスコープ解決演算子を使用します。

namespace A {
  namespace B {
    namespace C {
      int i;
    }
  }
}
// vs.
namespace A::B::C {
  int i;
}

構造化されたバインディング

非構造化の初期化に対する提案をします。これによって、 expr がタプルライクなオブジェクトで、その要素が変数 x y z (これらがコンストラクトを宣言します)にバインドされた auto {x, y, z} = expr; の書き込みを許可します。 タプルライクなオブジェクト には、 std::tuple std::pair std::array 、アグリゲート構造が含まれます。

using Coordinate = std::pair<int, int>;
Coordinate origin() {
  return Coordinate{0, 0};
}

const auto [ x, y ] = origin();
x; // == 0
y; // == 0

初期化子によるステートメントの選択

共通コードパターンを簡素化させ、ユーザがスコープを固定した状態に保つことを助ける、新しいバージョンの if switch です。

{
  std::lock_guard<std::mutex> lk(mx);
  if (v.empty()) v.push_back(val);
}
// vs.
if (std::lock_guard<std::mutex> lk(mx); v.empty()) {
  v.push_back(val);
}
Foo gadget(args);
switch (auto s = gadget.status()) {
  case OK: gadget.zip(); break;
  case Bad: throw BadFoo(s.message());
}
// vs.
switch (Foo gadget(args); auto s = gadget.status()) {
  case OK: gadget.zip(); break;
  case Bad: throw BadFoo(s.message());
}

constexpr if

コンパイル時条件次第でインスタンスが作成されるコードを書きます。

template <typename T>
constexpr bool isIntegral() {
  if constexpr (std::is_integral<T>::value) {
    return true;
  } else {
    return false;
  }
}
static_assert(isIntegral<int>() == true);
static_assert(isIntegral<char>() == true);
static_assert(isIntegral<double>() == false);
struct S {};
static_assert(isIntegral<S>() == false);

C++17のライブラリ機能

std::variant

クラステンプレート std::variant は、タイプセーフな union を表しています。いつでも std::variant のインスタンスは、それに取って代わる型の1つの値を保持します(それを無価値にすることも可能です)。

std::variant<int, double> v{ 12 };
std::get<int>(v); // == 12
std::get<0>(v); // == 12
v = 12.0;
std::get<double>(v); // == 12.0
std::get<1>(v); // == 12.0

std::optional

クラステンプレート std::optional は、値を含んだoptionalを管理します。言い換えれば、値は存在するかもしれませんし、存在しないかもしれません。optionalの一般的な使用事例として、機能しない可能性のある関数の戻り値があります。

std::optional<std::string> create(bool b) {
  if (b) {
    return "Godzilla";
  } else {
    return {};
  }
}

create(false).value_or("empty"); // == "empty"
create(true).value(); // == "Godzilla"
// optional-returning factory functions are usable as conditions of while and if
if (auto str = create(true)) {
  // ...
}

std::any

あらゆる型の単一値におけるタイプセーフコンテナです。

std::any x{ 5 };
x.has_value() // == true
std::any_cast<int>(x) // == 5
std::any_cast<int&>(x) = 10;
std::any_cast<int>(x) // == 10

std::string_view

ストリングへの非所有リファレンスです。ストリング上に抽象化を提供するのに便利です(例えば、パーシングに対してなど)。

// Regular strings.
std::string_view cppstr{ "foo" };
// Wide strings.
std::wstring_view wcstr_v{ L"baz" };
// Character arrays.
char array[3] = {'b', 'a', 'r'};
std::string_view array_v(array, sizeof array);
std::string str{ "   trim me" };
std::string_view v{ str };
v.remove_prefix(std::min(v.find_first_not_of(" "), v.size()));
str; //  == "   trim me"
v; // == "trim me"

std::invoke

パラメータと共に Callable オブジェクトを呼び出します。 Callable オブジェクトの例として、オブジェクトが通常の関数と同様に呼び出される std::function または std::bind が挙げられます。

template <typename Callable>
class Proxy {
    Callable c;
public:
    Proxy(Callable c): c(c) {}
    template <class... Args>
    decltype(auto) operator()(Args&&... args) {
        // ...
        return std::invoke(c, std::forward<Args>(args)...);
    }
};
auto add = [] (int x, int y) {
  return x + y;
};
Proxy<decltype(add)> p{ add };
p(1, 2); // == 3

std::apply

引数のタプルと共に Callable オブジェクトを呼び出します。

auto add = [] (int x, int y) {
  return x + y;
};
std::apply(add, std::make_tuple( 1, 2 )); // == 3

mapとsetのスプライシング

コストのかかるコピーや移動、ヒープの割り当てまたは割り当て解除などのオーバーヘッドなしで、ノードを移動したり、コンテナを統合したりします。

1つのmapから他のmapへと要素を移動するには以下のようにします。

::map<int, string> src{ { 1, "one" }, { 2, "two" }, { 3, "buckle my shoe" } };
std::map<int, string> dst{ { 3, "three" } };
dst.insert(src.extract(src.find(1))); // Cheap remove and insert of { 1, "one" } from `src` to `dst`.
dst.insert(src.extract(2)); // Cheap remove and insert of { 2, "two" } from `src` to `dst`.
// dst == { { 1, "one" }, { 2, "two" }, { 3, "three" } };

全てのsetを挿入するには以下のようにします。

std::set<int> src{1, 3, 5};
std::set<int> dst{2, 4, 5};
dst.merge(src);
// src == { 5 }
// dst == { 1, 2, 3, 4, 5 }

コンテナをより長生きさせる要素を挿入するには、以下のようにします。

auto elementFactory() {
  std::set<...> s;
  s.emplace(...);
  return s.extract(s.begin());
}
s2.insert(elementFactory());

map要素のキーを変更するには、以下のようにします。

std::map<int, string> m{ { 1, "one" }, { 2, "two" }, { 3, "three" } };
auto e = m.extract(2);
e.key() = 4;
m.insert(std::move(e));
// m == { { 1, "one" }, { 3, "three" }, { 4, "two" } }

C++14の言語機能

バイナリリテラル

バイナリリテラルは2進法の数を表すための便利な方法を提供します。 ' を使って桁を区切ることができます。

0b110 // == 6
0b1111'1111 // == 255

汎用ラムダ

C++14では、パラメータリストで auto 型指定子を使用できるようになり、多相的ラムダが可能になりました。

auto identity = [](auto x) { return x; };
int three = identity(3); // == 3
std::string foo = identity("foo"); // == "foo"

ラムダキャプチャの初期化子

これにより、任意の式で初期化されたラムダキャプチャを作成することが可能です。キャプチャされた値に与えられる名前は、エンクロージングスコープ内の変数と関連する必要はなく、ラムダ本体の内部で新しい名前が導入されます。初期化の式はラムダが( 呼び出された 時ではなく) 作成された 時に評価されます。

int factory(int i) { return i * 10; }
auto f = [x = factory(2)] { return x; }; // returns 20

auto generator = [x = 0] () mutable {
  // this would no compile without 'mutable' as we are modifying x on each call
  return x++;
};  
auto a = generator(); // == 0
auto b = generator(); // == 1
auto c = generator(); // == 2

従来はコピーまたは参照によってしかキャプチャできなかったラムダに、値を 移動 (または 転送 )することが可能になったので、値によりラムダ内にmove-only型をキャプチャすることができます。以下の例では、 = の左辺、 task2 のキャプチャリスト内の p は新しいラムダ本体のプライベート変数であり、元の p とは関係がないことにご注意ください。

auto p = std::make_unique<int>(1);

auto task1 = [=] { *p = 5; }; // ERROR: std::unique_ptr cannot be copied
// vs.
auto task2 = [p = std::move(p)] { *p = 5; }; // OK: p is move-constructed into the closure object
// the original p is empty after task2 is created

この参照キャプチャを使用すると、参照された変数と異なる名前を付けることができます。

auto x = 1;
auto f = [&r = x, x = x * 10] {
  ++r;
  return r + x;
};
f(); // sets x to 2 and returns 12

戻り値の型推論

C++14で戻り値の型 auto を使うと、コンパイラはその型を推論しようとします。ラムダでは、 auto を使って戻り値の型を推論することができ、推論された参照、あるいは右辺値参照を返すことが可能となります。

// Deduce return type as `int`.
auto f(int i) {
 return i;
}

template <typename T>
auto& f(T& t) {
  return t;
}

// Returns a reference to a deduced type.
auto g = [](auto& x) -> auto& { return f(x); };
int y = 123;
int& z = g(y); // reference to `y`  

decltype(auto)

decltype(auto) 型指定子は auto が行うような型も推論します。しかし、その一方でその参照や”定数性”を維持したまま、戻り値の型を推論します。これに対し auto ではこの推論はできません。

const int x = 0;
auto x1 = x; // int
decltype(auto) x2 = x; // const int
int y = 0;
int& y1 = y;
auto y2 = y; // int
decltype(auto) y3 = y; // int&
int&& z = 0;
auto z1 = std::move(z); // int
decltype(auto) z2 = std::move(z); // int&&

// Note: Especially useful for generic code!

// Return type is `int`.
auto f(const int& i) {
 return i;
}

// Return type is `const int&`.
decltype(auto) g(const int& i) {
 return i;
}

int x = 123;
static_assert(std::is_same<const int&, decltype(f(x))>::value == 0);
static_assert(std::is_same<int, decltype(f(x))>::value == 1);
static_assert(std::is_same<const int&, decltype(g(x))>::value == 1);

constexpr関数における制限の緩和

C++11では、 constexpr 関数本体には、 typedef using 、単一の return 文のような(でも、それだけに限定されない)非常に限られた構文のセットしか含めることができませんでした。C++14では、許容される構文のセットは拡張され、 if 文、複数の return 、loopのような最も一般的な構文を含むことができるようになりました。

constexpr int factorial(int n) {
  if (n <= 1) {
    return 1;
  } else {
    return n * factorial(n - 1);
  }
}
factorial(5); // == 120

C++14のライブラリ機能

標準ライブラリ型のためのユーザ定義リテラル

標準ライブラリ型のユーザ定義リテラルは、chronoと basic_string の新しいビルトインのリテラルを含む標準ライブラリ型の新しいユーザ定義リテラルです。これらは constexpr であり、つまりコンパイル時に使用されることになります。このようなリテラルの使い方には、コンパイル時の整数パース、バイナリリテラル、および虚数リテラルが含まれます。

using namespace std::chrono_literals;
auto day = 24h;
day.count(); // == 24
std::chrono::duration_cast<std::chrono::minutes>(day).count(); // == 1440

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

クラステンプレート std::integer_sequence はコンパイル時整数シーケンスを表します。ここには幾つかのヘルパが構築されています。

  • std::make_integer_sequence<T, N...> は、 T 型の 0, ..., N – 1 のシーケンスを作成します。
  • std::index_sequence_for<T...> はテンプレートパラメータパックを整数シーケンスに変換します。

以下のように、配列をタプルに変換します。

template<typename Array, std::size_t... I>
decltype(auto) a2t_impl(const Array& a, std::integer_sequence<std::size_t, I...>) {
  return std::make_tuple(a[I]...);
}

template<typename T, std::size_t N, typename Indices = std::make_index_sequence<N>>
decltype(auto) a2t(const std::array<T, N>& a) {
  return a2t_impl(a, Indices());    
}

C++11の言語機能

移動セマンティクス

移動セマンティクスは、主にパフォーマンスの最適化に関することであり、コピーのコストのかかるオーバーヘッドなしに移動することをいいます。コピーと移動の違いは、コピーではソースは変更されないのに対し、移動ではソースが変更されない、あるいは完全に異なったものになります。これは、ソースが何であるかに左右されます。POD型では移動とコピーは同じことになります。

オブジェクトを移動するということは、そのオブジェクトが管理する幾つかのリソースの所有権を他のオブジェクトに移動することを意味します。移動先のソースオブジェクトによって、ソースオブジェクトが保持しているポインタを移動させる、もしくは新たに保持するように変更することと考えることができます。つまり、リソースはメモリ内のその場所に留まるということです。このようなコストのかからないリソースの移動は、 rvalue のソースが移動後に変わってしまうといった副作用が起こる可能性がある場合にとても便利です。というのも、そのソースは一時的なオブジェクトで、後でアクセスすることができないからです。

移動により、オブジェクトをあるスコープから別のスコープに移動することも可能になります。例えば、 std::unique_ptr といった、一意のオブジェクトへのポインタを保持するような設計のスマートポインタなどです。

以下の右辺値参照、特殊メンバ関数の移動の定義、 std::move std::forward の各項をご覧ください。

右辺値参照

C++11では、 右辺値参照 と呼ばれる新しい参照が導入されており、 A に対する右辺値参照は A&& という構文で作成されます。これによって有効になる主要な機能は2つです。1つは移動セマンティクス、そしてもう1つは、引数に関する情報を一般的な方法で左辺値/右辺値として維持しながら渡すことができる Perfect Forward です。

左辺値および右辺値による auto 型推論:

int x = 0; // `x` is an lvalue of type `int`
int& xl = x; // `xl` is an lvalue of type `int&`
int&& xr = x; // compiler error -- `x` is an lvalue
int&& xr2 = 0; // `xr2` is an lvalue of type `int&&`
auto& al = x; // `al` is an lvalue of type `int&`
auto&& al2 = x; // `al2` is an lvalue of type `int&`
auto&& ar = 0; // `ar` is an lvalue of type `int&&`

関連項目: std::move std::forward

可変値引数テンプレート

... 構文は、 パラメータパック を作成または展開します。テンプレート パラメータパック とは、0個以上のテンプレート引数を受け入れるテンプレートパラメータです(非型、型、またはテンプレート)。少なくとも1つのパラメータパックを含むテンプレートのことを 可変値引数テンプレート と呼びます。

template <typename... T>
struct arity {
  constexpr static int value = sizeof...(T);
};
static_assert(arity<>::value == 0);
static_assert(arity<char, short, int>::value == 3);

初期化子リスト

“波カッコのリスト”構文で作成される配列に類似した軽量の要素のコンテナです。例えば、 { 1, 2, 3 } は、 std::initializer_list<int> 型を持つ整数のシーケンスを作成します。オブジェクトのvectorを関数に渡す代わりとして便利です。

int sum(const std::initializer_list<int>& list) {
  int total = 0;
  for (auto& e : list) {
    total += e;
  }

  return total;
}

auto list = { 1, 2, 3 };
f(list); // == 6
f({ 1, 2, 3 }); // == 6
f({}); // == 0

static_assert

アサーションが、コンパイル時に評価されます。

constexpr int x = 0;
constexpr int y = 1;
static_assert(x == y, "x != y");

auto

auto 型の変数が、その初期化子の型に従ってコンパイラにより推論されます。

auto a = 3.14; // double
auto b = 1; // int
auto& c = b; // int&
auto d = { 0 }; // std::initializer_list<int>
auto&& e = 1; // int&&
auto&& f = b; // int&
auto g = new auto(123); // int*
const auto h = 1; // const int
auto i = 1, j = 2, k = 3; // int, int, int
auto l = 1, m = true, n = 1.61; // error -- `l` deduced to be int, `m` is bool
auto o; // error -- `o` requires initializer

特に複雑な型の場合、可読性が向上し非常に便利です。

std::vector<int> v = ...;
std::vector<int>::const_iterator cit = v.cbegin();
// vs.
auto cit = v.cbegin();

関数が auto を利用することで、戻り値の型を推論することもできます。C++11では、戻り値の型は明示的に指定するか、あるいは以下のように decltype を使わなければなりません。

template <typename X, typename Y>
auto add(X x, Y y) -> decltype(x + y) {
  return x + y;
}
add(1, 2); // == 3
add(1, 2.0); // == 3.0
add(1.5, 1.5); // == 3.0

上の例にある後続の戻り値の型は、 x + y 式の 宣言型 decltype の項を参照)です。例えば、 x が整数で y がdoubleの場合、 decltype(x + y) はdoubleとなります。従って上記の関数は、 x + y 式がどのような型を取るかによって型を推論します。なお、後続の戻り値の型はそのパラメータ、および該当する場合は this を取得します。

ラムダ式

lambda は、スコープ内の変数を取り込める無名関数オブジェクトで、その機能として キャプチャリスト 、オプションの後続の戻り値の型を持つオプションのパラメータセット、そしてボディなどが挙げられます。キャプチャリストの例は以下の通りです。

  • [] – 何もキャプチャしない。
  • [=] – 値によってスコープ内のローカルオブジェクト(ローカル変数、パラメータ)をキャプチャする。
  • [&] – 参照によってスコープ内のローカルオブジェクト(ローカル変数、パラメータ)をキャプチャする。
  • [this] – 値によって this ポインタをキャプチャする。
  • [a, &b] – 値によってオブジェクト a を、参照によってオブジェクト b をキャプチャする。
int x = 1;

auto getX = [=]{ return x; };
getX(); // == 1

auto addX = [=](int y) { return x + y; };
addX(1); // == 2

auto getXRef = [&]() -> int& { return x; };
getXRef(); // int& to `x`

デフォルトでは、コンパイラ生成メソッドが const としてマークされているため、値のキャプチャはラムダ内では変更できません。ただし、 mutable キーワードを使用すると、キャプチャされた変数の変更が可能です。キーワードはパラメータリスト(空であっても記述は必要)の後に置きます。

int x = 1;

auto f1 = [&x] { x = 2; }; // OK: x is a reference and modifies the original

auto f2 = [x] { x = 2; }; // ERROR: the lambda can only perform const-operations on the captured value
// vs.
auto f3 = [x] () mutable { x = 2; }; // OK: the lambda can perform any operations on the captured value

decltype

decltype は、渡された式の宣言型を返す演算子です。 decltype の例は以下の通りです。

int a = 1; // `a` is declared as type `int`
decltype(a) b = a; // `decltype(a)` is `int`
const int& c = a; // `c` is declared as type `const int&`
decltype(c) d = a; // `decltype(c)` is `const int&`
decltype(123) e = 123; // `decltype(123)` is `int`
int&& f = 1; // `f` is declared as type `int&&`
decltype(f) g = 1; // `decltype(f) is `int&&`
decltype((a)) h = x; // `decltype((a))` is int&
template <typename X, typename Y>
auto add(X x, Y y) -> decltype(x + y) {
  return x + y;
}
add(1, 2.0); // `decltype(x + y)` => `decltype(3.0)` => `double`

エイリアステンプレート

typedef を使うのに意味的には似ていますが、 using によるテンプレートエイリアスは可読性が高く、テンプレートと互換性があります。

template <typename T>
using Vec = std::vector<T>;
Vec<int> v{}; // std::vector<int>

using String = std::string;
String s{"foo"};

nullptr

C++11では、Cの NULL マクロを置き換えるために設計された新しいNULLポインタ型が導入されています。 nullptr 自体は std::nullptr_t 型で、暗黙的にポインタ型に変換できますが、 NULL とは違い bool 以外の整数型には変換できません。

void foo(int);
void foo(char*);
foo(NULL); // error -- ambiguous
foo(nullptr); // calls foo(char*)

厳密に型指定された列挙型

タイプセーフな列挙型が解決するCスタイル列挙型の様々な問題の中には、暗黙的な変換、基盤となる型を指定できないこと、スコープの汚染が含まれます。

// Specifying underlying type as `unsigned int`
enum class Color : unsigned int { Red = 0xff0000, Green = 0xff00, Blue = 0xff };
// `Red`/`Green` in `Alert` don't conflict with `Color`
enum class Alert : bool { Red, Green };
Color c = Color::Red;

属性

属性は、 __attribute__(...) __declspec などの汎用構文を提供します。

// `noreturn` attribute indicates `f` doesn't return.
[[ noreturn ]] void f() {
  throw "error";
}

constexpr

定数式は、コンパイル時にコンパイラによって評価される式です。定数式では、複雑ではない計算だけが実行できます。 constexpr 指定子により、変数や関数などが定数式であることを示します。

constexpr int square(int x) {
  return x * x;
}

int square2(int x) {
  return x * x;
}

int a = square(2);  // mov DWORD PTR [rbp-4], 4

int b = square2(2); // mov edi, 2
                    // call square2(int)
                    // mov DWORD PTR [rbp-8], eax

constexpr 値は、コンパイラがコンパイル時に評価できます。

const int x = 123;
constexpr const int& y = x; // error -- constexpr variable `y` must be initialized by a constant expression

クラスを持つ定数式は以下の通りです。

struct Complex {
  constexpr Complex(double r, double i) : re(r), im(i) { }
  constexpr double real() { return re; }
  constexpr double imag() { return im; }

private:
  double re;
  double im;
};

constexpr Complex I(0, 1);

コンストラクタのデリゲート

コンストラクタは、初期化子リストを利用して同じクラス内の別のコンストラクタを呼び出せるようになりました。

struct Foo {
  int foo;
  Foo(int foo) : foo(foo) {}
  Foo() : Foo(0) {}
};

Foo foo{};
foo.foo; // == 0

ユーザ定義リテラル

ユーザ定義リテラルにより、言語を拡張し、独自の構文を追加できます。リテラルを作成するには、 X の名前で型 T を返す T operator "" X(...) { ... } 関数を定義します。ちなみに、この関数の名前がリテラルの名前を定義することに留意しておいてください。アンダースコアで始まらないリテラル名は保留され、呼び出されません。ユーザ定義のリテラル関数が受け入れるべきパラメータには、リテラルがどの型で呼び出されているかによるルールがあります。

摂氏から華氏への変換:

// `unsigned long long` parameter required for integer literal.
long long operator "" _celsius(unsigned long long tempCelsius) {
  return std::llround(tempCelsius * 1.8 + 32);
}
24_celsius; // == 75

文字列から整数への変換:

// `const char*` and `std::size_t` required as parameters.
int operator "" _int(const char* str, std::size_t) {
  return std::stoi(str);
}

"123"_int; // == 123, with type `int`

明示的な仮想オーバーライド

仮想関数が別の仮想関数をオーバーライドするという明示をします。仮想関数が親の仮想関数をオーバーライドしない場合、コンパイラエラーがスローされます。

struct A {
  virtual void foo();
  void bar();
};

struct B : A {
  void foo() override; // correct -- B::foo overrides A::foo
  void bar() override; // error -- A::bar is not virtual
  void baz() override; // error -- B::baz does not override A::baz
};

既定化関数

コンストラクタなどのような関数の既定化を提供する、より効率的で洗練された方法です。

struct A {
  A() = default;
  A(int x) : x(x) {}
  int x{ 1 };
};
A a{}; // a.x == 1
A a2{ 123 }; // a.x == 123
With inheritance:
struct B {
  B() : x(1);
  int x;
};

struct C : B {
  // Calls B::B
  C() = default;
};

C c{}; // c.x == 1

削除指定関数

関数の削除指定を提供する、より効率的で洗練された方法です。オブジェクトへのコピーを回避するのに有効です。

class A {
  int x;

public:
  A(int x) : x(x) {};
  A(const A&) = delete;
  A& operator=(const A&) = delete;
};

A x{ 123 };
A y = x; // error -- call to deleted copy constructor
y = x; // error -- operator= deleted

範囲ベースのfor loop

コンテナの要素を反復する糖衣構文です。

std::array<int, 5> a{ 1, 2, 3, 4, 5 };
for (int& x : a) x *= 2;
// a == { 2, 4, 6, 8, 10 }

int を使う際には int& との違いに注意してください。

std::array<int, 5> a{ 1, 2, 3, 4, 5 };
for (int x : a) x *= 2;
// a == { 1, 2, 3, 4, 5 }

移動セマンティクスのための特殊メンバ関数

コピーが作成されるとコピーコンストラクタおよびコピー代入演算子が呼び出されますが、C++11では移動セマンティクスの導入により、移動用に移動コンストラクタと移動代入演算子があります。

struct A {
  std::string s;
  A() : s("test") {}
  A(const A& o) : s(o.s) {}
  A(A&& o) : s(std::move(o.s)) {}
  A& operator=(A&& o) {
   s = std::move(o.s);
   return *this;
  }
};

A f(A a) {
  return a;
}

A a1 = f(A{}); // move-constructed from rvalue temporary
A a2 = std::move(a1); // move-constructed using std::move
A a3 = A{};
a2 = std::move(a3); // move-assignment using std::move
a1 = f(A{}); // move-assignment from rvalue temporary

C++11ライブラリ機能

std::move

std::move は、渡されたオブジェクトが移動されること、言い換えるなら、コピーすることなく、あるオブジェクトから別のオブジェクトに移動されることを示します。渡されたオブジェクトは移動後、特定の状況下では使用してはなりません。

std::move の定義(移動の実行は、右辺値にキャストするといった程度の意味です):

template <typename T>
typename remove_reference<T>::type&& move(T&& arg) {
  return static_cast<typename remove_reference<T>::type&&>(arg);
}

std::unique_ptr を移動:

std::unique_ptr<int> p1{ new int };
std::unique_ptr<int> p2 = p1; // error -- cannot copy unique pointers
std::unique_ptr<int> p3 = std::move(p1); // move `p1` into `p2`
                                         // now unsafe to dereference object held by `p1`

std::forward

渡された引数を、左辺値あるいは右辺値のいずれかとしてそのまま返し、cv修飾を含みます。該当する場合、例えばファクトリのように、参照(左辺値または右辺値のいずれか)が必要な汎用コードに便利です。転送は テンプレート引数の推論 によって可能になります。

  • T& & T& になる。
  • T& && T& になる。
  • T&& & T& になる。
  • T&& && T&& になる。

std::forward の定義:

template <typename T>
T&& forward(typename remove_reference<T>::type& arg) {
  return static_cast<T&&>(arg);
}

単に、他の A オブジェクトを新しい A オブジェクトのコピーまたは移動コンストラクタに転送する wrapper 関数の例:

struct A {
  A() = default;
  A(const A& o) { std::cout << "copied" << std::endl; }
  A(A&& o) { std::cout << "moved" << std::endl; }
};

template <typename T>
A wrapper(T&& arg) {
  return A{ std::forward<T>(arg) };
}

wrapper(A{}); // moved
A a{};
wrapper(a); // copied
wrapper(std::move(a)); // moved

std::to_string

数値の引数を std::string に変換します。

std::to_string(1.2); // == "1.2"
std::to_string(123); // == "123"

型特性

型特性は、型のプロパティを照会または変更するための、コンパイル時におけるテンプレートベースのインターフェースを定義します。

static_assert(std::is_integral<int>::value == 1);
static_assert(std::is_same<int, int>::value == 1);
static_assert(std::is_same<std::conditional<true, int, double>::type, int>::value == 1);

スマートポインタ

C++11では、 std::unique_ptr std::shared_ptr std::weak_ptr という新しい(改善された)スマートポインタが導入されました。 std::auto_ptr は現在では非推奨になっており、最終的にC++17で廃止されています。 std::unique_ptr はコピー不可の移動可能なスマートポインタで、配列とSTLコンテナを適切に管理します。

std::unique_ptr<Foo> p1(new Foo);  // `p1` owns `Foo`
if (p1) p1->bar();

{
  std::unique_ptr<Foo> p2(std::move(p1));  // Now `p2` owns `Foo`
  f(*p2);

  p1 = std::move(p2);  // Ownership returns to `p1` -- `p2` gets destroyed
}

if (p1) p1->bar();
// `Foo` instance is destroyed when `p1` goes out of scope

std::chrono

chronoライブラリには、 持続時間時計タイムポイント を扱うユーティリティ関数と型のセットが含まれています。このライブラリの使用例の1つにベンチマークコードがあります。

std::chrono::time_point<std::chrono::system_clock> start, end;
start = std::chrono::system_clock::now();
// Some computations...
end = std::chrono::system_clock::now();

std::chrono::duration<double> elapsed_seconds = end-start;

elapsed_seconds.count(); // t number of seconds, represented as a `double`

タプル

タプルは、異なる値の固定サイズコレクションです。 std::tie または std::get を使ってアンパックし、 td::tuple の要素にアクセスします。

// `playerProfile` has type `std::tuple<int, std::string, std::string>`.
auto playerProfile = std::make_tuple(51, "Frans Nielsen", "NYI");
std::get<0>(playerProfile); // 51
std::get<1>(playerProfile); // "Frans Nielsen"
std::get<2>(playerProfile); // "NYI"

std::tie

左辺値参照のタプルを作成します。 std::pair および std::tuple オブジェクトをアンパックするのに便利です。無視される値に関しては、プレースホルダとして std::ignore を使用します。C++17では、代わりに構造化束縛を使う必要があります。

// With tuples...
std::string playerName;
std::tie(std::ignore, playerName, std::ignore) = std::make_tuple(91, "John Tavares", "NYI");

// With pairs...
std::string yes, no;
std::tie(yes, no) = std::make_pair("yes", "no");

std::array

std::array は、Cスタイル配列の上に構築されるコンテナです。並べ替えなどの一般的なコンテナ操作をサポートします。

std::array<int, 3> a = {2, 1, 3};
std::sort(a.begin(), a.end()); // a == { 1, 2, 3 }
for (int& x : a) x *= 2; // a == { 2, 4, 6 }

unorderedコンテナ

これらのコンテナは検索、挿入、削除操作に関して、平均した一定の時間計算量を維持します。バケツに要素をハッシングすることで速度を犠牲にし、一定の時間計算量を実現しています。unorderedコンテナは以下の4種類です。

  • unordered_set
  • unordered_multiset
  • unordered_map
  • unordered_multimap

メモリモデル

C++11ではC++のメモリモデルが導入されており、スレッド化とアトミック操作ライブラリをサポートしています。これらの操作の一部(全てではありません)には、アトミックロード/ストア、コンペア・アンド・スワップ、アトミックフラグ、FutureやPromise、ロック、条件変数などが含まれます

謝辞

著者

Anthony Calandra

コンテンツコントリビュータ

新規コンテンツを追加したユーザが、ここに順不同でリストされています。

  • thukydides – 2進数桁区切り文字に言及。
  • mknejp – ラムダキャプチャ初期化子および mutable キーワード。

使用許諾

MIT

監修者
監修者_古川陽介
古川陽介
株式会社リクルート プロダクト統括本部 プロダクト開発統括室 グループマネジャー 株式会社ニジボックス デベロップメント室 室長 Node.js 日本ユーザーグループ代表
複合機メーカー、ゲーム会社を経て、2016年に株式会社リクルートテクノロジーズ(現リクルート)入社。 現在はAPソリューショングループのマネジャーとしてアプリ基盤の改善や運用、各種開発支援ツールの開発、またテックリードとしてエンジニアチームの支援や育成までを担う。 2019年より株式会社ニジボックスを兼務し、室長としてエンジニア育成基盤の設計、技術指南も遂行。 Node.js 日本ユーザーグループの代表を務め、Node学園祭などを主宰。