#Anthony Calandra / モダンC++の機能

(注: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 ...)の式を単項フォールドと呼ぶ。
  • op1op2がフォールド演算子である(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がタプルライクなオブジェクトで、その要素が変数xyz(これらがコンストラクトを宣言します)にバインドされたauto {x, y, z} = expr;の書き込みを許可します。タプルライクなオブジェクトには、std::tuplestd::pairstd::array、アグリゲート構造が含まれます。

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

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

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

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

{
  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関数本体には、typedefusing、単一の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::movestd::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::movestd::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_ptrstd::shared_ptrstd::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