短いリスト:
- 通常の関数の戻り値の型を自動的に検出
- Capture-by-Moveをサポートするキャプチャされたラムダ変数の一般的な初期化
- 一般化された(多態性)ラムダ式
-
constexpr
関数の作成に関する単純化された制限 - 可変テンプレート
-
exchange
-
make_unique
- 行を分ける
- 標準ライブラリタイプのカスタムリテラル
-
optional
-
shared_mutex
およびshared_lock
-
dynarray
言語自体の変更
通常の関数の戻り値の型を自動的に検出
C ++ 11以降、この言語にはラムダ式を定義する機能があります。そのため、
return
ステートメントが1つしかない場合、戻り値のタイプを指定できません。コンパイラーは自分で出力できます。 多くの人は、この機能が通常の機能では利用できないことに驚いていました。 対応する提案はC ++ 11のリリース前でも行われていましたが、時間の制限により延期され、数年後には標準に追加されました。 また、この修正では、関数またはラムダ式に同じ型を返す
return
が複数ある場合、この場合、コンパイラは再帰呼び出しの場合も含めて、型の値も自動的に出力する必要があると述べています。
auto iterate(int len) // - int { for (int i = 0; i < len; ++i) if (search (i)) return i; return -1; } auto h() { return h(); } // , auto sum(int i) { if (i == 1) return i; // - int else return sum(i-1)+i; // } template <class T> auto f(T t) { return t; } // []()->auto& { return f(); } //
この機能には大きな可能性があるにもかかわらず、いくつかの制限があります。
関数宣言をヘッダーファイルに配置し、定義がソースコードを含む対応するファイルにある場合、ヘッダーファイルが他のファイルに接続されていると、コンパイラーは型を推測できません。
// foo.h class Foo { public: auto getA() const; }; // foo.cpp #include "foo.h" auto Foo::getA() const { return something; } // main.cpp #include "foo.h" int main() { Foo bar; auto a = bar.getA(); // , }
つまり、これを使用すると、ローカル関数またはヘッダーファイルで定義された関数でのみ機能します。 後者には、原則として、テンプレート関数が含まれます-私の意見では、メインはこの新製品のアプリケーションです。
標準で定義されている制限には、仮想関数での使用の禁止、およびタイプ
std::initializer_list
オブジェクトを返すことの禁止が含まれ
std::initializer_list
。
Capture-by-Moveをサポートするキャプチャされたラムダ変数の一般的な初期化
C ++ 11では、ラムダは移動によるキャプチャをサポートしていません。 たとえば、次のコードはコンパイルされません。
#include <memory> #include <iostream> #include <utility> template <class T> void run(T&& runnable) { runnable(); }; int main() { std::unique_ptr<int> result(new int{42}); run([result](){std::cout << *result << std::endl;}); }
関数ラムダ内でオブジェクトを移動するには、廃止されたauto_ptrのような何らかのラッパーを記述する必要がありました。
明示的に移動してキャプチャする機能を追加する代わりに、キャプチャされた変数の一般化された初期化のサポートを追加することが決定されました。 例えば
[ x { move(x) }, y = transform(y, z), foo, bar, baz ] { ... }
この場合、
x
は
x
を移動することによって直接初期化され、
y
は
transform
を呼び出した結果によって初期化され、残りは値によってキャプチャされます。 別の例:
int x = 4; auto y = [&r = x, x = x+1]()->int { r += 2; return x+2; }(); // ::x 6, y 7-.
また、初期化されたキャプチャ変数のタイプを明示的に示すことも禁止されていません。
[int x = get_x()] { ... } [Container y{get_container()}] { ... } [int z = 5] { ... }
多くの人は、なぜ次の方法をサポートしていないのだろうと思うかもしれません
[&&x] { ... }
問題は、右辺値リンクをキャプチャしていないことで、移動しようとしていることです。 この方法で行った場合、実際のオブジェクトがもはや存在しない可能性があるときに、移動がずっと遅く発生する可能性があります。 ラムダを呼び出している間ではなく、変数をキャプチャしている間にオブジェクトを移動する必要があります。
一般化された(多態性)ラムダ式
C ++ 11では、ラムダ式は、非標準の関数呼び出し演算子を持つクラスのオブジェクトを作成します。 この修正案は以下を提案します。
- ジェネリック(テンプレート)ラムダパラメーターを意味する
auto
型指定子の使用を許可する - 値をキャプチャしないラムダ関数から対応する関数ポインターへの変換を許可します
したがって、同じテンプレートラムダを異なるコンテキストで使用できます。
void f1(int (*)(int)) { } void f2(char (*)(int)) { } void g(int (*)(int)) { } // #1 void g(char (*)(char)) { } // #2 void h(int (*)(int)) { } // #3 void h(char (*)(int)) { } // #4 auto glambda = [](auto a) { return a; }; f1(glambda); // OK f2(glambda); // : ID g(glambda); // : h(glambda); // OK: #3, ID int& (*fpi)(int*) = [](auto* a) -> auto& { return *a; }; // OK
ユニバーサルリンクとテンプレートを使用して、 可変数の引数(可変テンプレート)を使用する可能性を忘れないでください。
auto vglambda = [](auto printer) { return [=](auto&& ... ts) { // OK: ts - printer(std::forward<decltype(ts)>(ts)...); }; }; auto p = vglambda( [](auto v1, auto v2, auto v3) { std::cout << v1 << v2 << v3; } ); p(1, 'a', 3.14); // OK: 1a3.14
constexpr
関数の作成に関する単純化された制限
導入された変更により、
constexpr
関数で使用できるようになりました。
- 変数宣言(
static
、thread_local
および初期化されていない変数を除く) -
if
およびswitch
(ただしgoto
はありません) -
for
(範囲ベースのfor
を含む)、while
およびdo-while
-
constexpr
計算から生じるオブジェクトの状態の変化
constexpr int abs(int x) { if (x < 0) x = -x; return x; // OK } constexpr int first(int n) { static int value = n; // : return value; } constexpr int uninit() { int a; // : return a; } constexpr int prev(int x) { return --x; } // OK constexpr int g(int x, int n) { // OK int r = 1; while (--n > 0) r *= x; return r; }
さらに、
constexpr
非静的メンバー関数が暗黙的に
const
修飾子を受け取るルールが削除されました(詳細はこちら )。
可変テンプレート
この機能を使用すると、
constexpr
変数テンプレートを作成および使用して、テンプレートアルゴリズムとのより便利な組み合わせを実現できます(組み込み型だけでなく、ユーザーが定義した型でも使用できます)。
template<typename T> constexpr T pi = T(3.1415926535897932385); template<typename T> T circular_area(T r) { return pi<T> * r * r; } struct matrix_constants { template<typename T> using pauli = hermitian_matrix<T, 2>; template<typename T> constexpr pauli<T> sigma1 = { { 0, 1 }, { 1, 0 } }; template<typename T> constexpr pauli<T> sigma2 = { { 0, -1i }, { 1i, 0 } }; template<typename T> constexpr pauli<T> sigma3 = { { 1, 0 }, { -1, 0 } }; };
標準ライブラリの変更
exchange
アトミックオブジェクトは、オブジェクトに新しい値を割り当てて古い値を返すことができる
atomic_exchange
関数を提供します。 同様の関数は、通常のオブジェクトに役立ちます。
// template<typename T, typename U=T> T exchange(T& obj, U&& new_val) { T old_val = std::move(obj); obj = std::forward<U>(new_val); return old_val; }
プリミティブ型の場合、この関数は通常の実装と同じように機能します。 複合型の場合、この関数を使用すると次のことができます。
- このタイプに移動コンストラクターが定義されている場合は、古いオブジェクトをコピーしないでください
- 変換代入演算子を使用することを利用して、任意のタイプを新しい値として受け入れます
- 新しいオブジェクトが一時的なものであるか、ここに移動されている場合は、コピーしないでください
たとえば、
std::unique_ptr::reset
実装:
template<typename T, typename D> void unique_ptr<T, D>::reset(pointer p = pointer()) { pointer old = ptr_; ptr_ = p; if (old) deleter_(old); }
次のように改善できます。
template<typename T, typename D> void unique_ptr<T, D>::reset(pointer p = pointer()) { if (pointer old = std::exchange(ptr_, p)) deleter_(old); }
make_unique
C ++ 11では、スマートポインターとともに、
make_shared
などの関数
make_shared
。 これにより、
shared_ptr
のメモリの割り当てを最適化し、例外に関するセキュリティを向上させることができます。
unique_ptr
に対して同様の最適化を行うこと
unique_ptr
できませんが、例外に関するセキュリティの向上は決して損なわれません。 たとえば、どちらの場合でも、1つのオブジェクトの作成時に例外がスローされ、2番目のオブジェクトが既に作成されているが、
unique_ptr
まだ配置されていない場合、メモリリークが発生します。
void foo(std::unique_ptr<A> a, std::unique_ptr<B> b); int main() { foo(new A, new B); foo(std::unique_ptr<A>{new A}, foo(std::unique_ptr<B>{new B}); }
この状況を解決するために、
make_unique
関数を追加することにしました。 したがって、C ++ 14は(非常にまれな場合を除いて)ほぼ完全に、プログラマーに
new
演算子を放棄して
delete
演算子を提供します。
使用例:
#include <iostream> #include <string> #include <memory> using namespace std; void foo(std::unique_ptr<string> a, std::unique_ptr<string> b) {} int main() { cout << *make_unique<int>() << endl; cout << *make_unique<int>(1729) << endl; cout << "\"" << *make_unique<string>() << "\"" << endl; cout << "\"" << *make_unique<string>("meow") << "\"" << endl; cout << "\"" << *make_unique<string>(6, 'z') << "\"" << endl; auto up = make_unique<int[]>(5); for (int i = 0; i < 5; ++i) { cout << up[i] << " "; } cout << endl; foo(make_unique<string>(), make_unique<string>()); // auto up1 = make_unique<string[]>("error"); // auto up2 = make_unique<int[]>(10, 20, 30, 40); // auto up3 = make_unique<int[5]>(); // auto up4 = make_unique<int[5]>(11, 22, 33, 44, 55); // } /* Output: 0 1729 "" "meow" "zzzzzz" 0 0 0 0 0 */
行を分ける
入力/出力ストリームでスペースを含む文字列を使用する場合、常に予想される結果とはほど遠いものが得られます。 例:
std::stringstream ss; std::string original = "foolish me"; std::string round_trip; ss << original; ss >> round_trip; std::cout << original; // : foolish me std::cout << round_trip; // : foolish assert(original == round_trip); // assert
この修正により、書き込み時に行の先頭と末尾に二重引用符を追加し、読み取り時にそれらを削除することにより、このような状況を正しく処理できる機能が標準に導入されます。 例:
std::stringstream ss; std::string original = "foolish me"; std::string round_trip; ss << quoted(original); ss >> quoted(round_trip); std::cout << original; // : foolish me std::cout << round_trip; // : foolish me assert(original == round_trip); // assert
文字列自体に引用符が既に含まれている場合、それらも分離されます。
std::cout << "She said \"Hi!\""; // : She said "Hi!" std::cout << quoted("She said \"Hi!\""); // : "She said \"Hi!\""
この修正はブーストカウンターパートに基づいています。
標準ライブラリタイプのカスタムリテラル
C ++ 11はユーザーリテラル(PL)の概念を導入しますが、標準では、アンダースコアで始まるPL名はSTL用に予約されていますが、単一のPLは標準ライブラリ専用に定義されていません。
対応する修正により、次のユーザーリテラルが標準に追加されます。
-
basic_string
s
演算子 - クロノに含まれるタイプの演算子
h
、min
、s
、ms
、us
、ns
:: duration
auto mystring = "hello world"s; // std::string auto mytime = 42ns; // chrono::nanoseconds
ユーザー
s
リテラルは、異なるタイプのパラメーターを受け入れるため、競合しません。
optional
この修正により、新しいタイプが標準ライブラリに導入され、オプションのオブジェクトが示されます。 可能な用途:
- オプションの関数パラメーターを指定する機能
- null状態を使用する機能(通常の(生の)ポインターを使用せずに)
- RAIIオブジェクトのライフタイムを手動で制御する機能
- 高価な(標準)オブジェクトコンストラクターをスキップする機能
おおよその使用方法:
optional<int> str2int(string); // , int get_int_form_user() { string s; for (;;) { cin >> s; optional<int> o = str2int(s); // 'o' , if (o) { // 'o' ? return *o; // } } }
この修正はブーストカウンターパートに基づいています。
このクラスに関するこの修正の著者による記事の翻訳はここにあります 。
shared_mutex
およびshared_lock
マルチスレッドプログラムを開発する場合、複数のオブジェクトにオブジェクトへの読み取りアクセスまたは一意の書き込みアクセスを許可することが必要になる場合があります。 この修正により、この目的のために設計された標準の
shared_mutex
ライブラリが追加されます。
lock
関数は一意のアクセスを提供し、以前に追加された
lock_guard
および
unique_lock
で使用できます。 共有アクセスを取得するには、
lock_shared
関数を使用する必要があり、追加されたRAII
shared_lock
クラスを使用してこれを行うことをお
shared_lock
ます。
using namespace std; shared_mutex rwmutex; { shared_lock<shared_mutex> read_lock(rwmutex); // } { unique_lock<shared_mutex> write_lock(rwmutex); // lock_guard // }
ロックのコストは、読み取りであっても、通常のミューテックスを使用するよりも高価であることに注意してください。
この修正はブーストカウンターパートに基づいています。
dynarray
現在のC標準には、実行時にのみサイズを決定できる動的配列があります。 通常の配列と同様に、作成されるデータはスタック上にあります。 C ++ 14はそのような配列を主張しますが、独自の種類の動的配列も定義します
std::dynarray
も実行時にサイズを認識し、後で変更することはできませんが、スタック上のオブジェクトを配置することも、または、
std::array
および
std::vector
似た多くの方法でインターフェースを持ちながら、それらをヒープに
std::array
します。 修正案によれば、このクラスは、スタックを使用する場合のために、コンパイラーによって明示的に最適化できます。
おわりに
基本的に、C ++ 14はマイナーなバグ修正リリースとして位置付けられており、時間制限またはその他の理由により生じたC ++ 11の欠点を修正しています。 C ++標準の現在のドラフトはここにあります 。
更新:
dynarray
と
optional
は別々の技術仕様に入れられました。
新しいC ++ 14機能の概要:パート2