C++学習ノート(いくつかの新しい特性まとめ1)

6933 ワード

C++学習ノート(いくつかの新しい特性まとめ1)
私もC++を使って何年も経っていますが、ずっと十分に使えばいいという原則に基づいて、特にC++の文法を深く勉強したことがないので、多くの高級なC++の特性が分かりません.ちょうど最近ネット上で本《C++14 Quick Syntax Reference》を探し当てて、とても薄い1冊の本、100数ページしかなくて、しかし基本的なすべてのC++の特性をカバーしました.この短文は私がこの本を読んだときに抜粋した私が以前気づかなかった知識点です.
本文中のすべてのコードはgcc version 5.3.0(Rev 1,Built by MSYS 2 project)でテストに合格した.
16進、8進、2進表示
int myOct = 062;
int myHex = 0x32;
int myBin = 0b00110010;

このうち16進法、8進法は早くからサポートされている特性です.2進数表示はC++14で正式にサポートされています.このほか、C++14には、数字表示の区切り記号として単一引用符が導入されている.長い数字を読むのに便利です.たとえば次の例です.
int longBin = 0b1010'0101'1010'0101;

分割として3つの引用符を付けると、読みやすくなります.
NULLポインタ(nullptr)
初期のC++では,無効なポインタアドレスを0またはNULLで表した.C++11は、NULLポインタを表す新しいキーワードnullptrを特別に導入した.
int* p = nullptr; // ok

nullptrにはnullptr_というタイプがありますt:
nullptr_t mynull = nullptr; // ok

右参照タイプ(C++11)
左値と右値は式の場合、左値は式の終了後も存在する永続的なオブジェクトを指し、右値は式の終了時に存在しない一時的なオブジェクトを指します.
右参照とは、次の例のように、右オブジェクト(一時オブジェクト)を参照することです.
int&& ref = 1 + 2; // rvalue reference
ref += 3;
cout << ref; // "6"

refは1+2の結果3に対応し,これは一時的なオブジェクトである.従来のC++参照方式では、この一時オブジェクトは参照できません.右の値の参照は主に効率の問題を解決して、具体的な方法は“C++移動の意味”を検索することができます.
オリジナル文字列(raw string literals)
raw stringはエスケープ文字の役割を取り消すことができ、C++11に追加された新しい特性です.例:
string escaped = "c:\\Windows\\System32\\cmd.exe";

以下のように簡単に書くことができます.
string raw = R"(c:\Windows\System32\cmd.exe)";  

RAW Stringは、最初に大文字のRを使用し、二重引用符に括弧を1対追加します.
この機能は他の言語でとっくに存在している.上の例ではraw stringの優位性はあまり見られませんが、正規表現をよく書くとraw stringが便利すぎると感じます.例:
char str[] = R"(('(?:[^\\']|\\.)*'|"(?:[^\\"]|\\.)*")|)";

古い書き方は次のとおりです.
char oldstr[] = "('(?:[^\\\\']|\\\\.)*'|\"(?:[^\\\\\"]|\\\\.)*\")|";

新しいcharタイプ
C++11にchar 16_が導入されたtとchar 32_tの2種類.この2つの文字タイプは、utf−16およびutf−32符号化された文字をそれぞれ格納するために使用することができる.対応するstringタイプにも2つの変種があり、u 16 stringとu 32 stringである.
string s3 = u8"UTF-8 string";
u16string s4 = u"UTF-16 string";
u32string s5 = U"UTF-32 string";

文字列の前のu 8、u、およびUも、UTF-8、UTF-16、およびUTF-32文字列をサポートするために追加された特性である.
forサイクル
C++11はforループを拡張し,forループは新しい構文をサポートする.
int a[3] = {1, 2, 3};
for (int &i : a) 
{
    cout <<i; // "123"
}

autoとdecltypeキーワード
どちらのキーワードもC++11で導入されています.Autoキーワードは、コンパイラが変数のタイプを自動的に導くことを示します.たとえば、次のコードがあります.
auto i = 5; // int
auto d = 3.14; // double
auto b = false; // bool

導出したいタイプが参照タイプである場合.ではautoの後に&を追加する必要があります.たとえば、次のようになります.
int& iRef = i;
auto myAuto = iRef; // int
auto& myRef = iRef; // int&

autoを使用すると、多くのコードを簡略化できます.たとえば、次のコードがあります.
vector<int> myVector { 1, 2, 3 };
for(vector<int>::size_type i = 0; i != myVector.size(); i++) 
{
    cout << myVector[i]; 
}

オートを使うと次のように書くことができます.
for(auto i = 0; i != myVector.size(); i++) 
{
    cout << myVector[i]; 
}

もちろん、forの新しい文法を使って、もっと簡単に書くことができます.
for (auto& x : myVector) 
{ 
    cout << x << endl; 
} 

decltypeはautoと似ています.次の例のように、式のタイプを導くために使用できます.
int a = 0b10'001'000;
cout << a << endl;
decltype(a) c = a + 1; //int
cout << c << endl;

decltype(3) b = 3; // int&&

説明する必要があるのは、この例の3は一時変数である.したがって、bのタイプはint型の右値参照であると推定される.しかし関数として値を返す場合,導出されるのは右値参照ではない.
decltype(5) getFive() { return 5; } // int

C++11 autoとdecltypeは、関数の戻り値タイプを導くために併用することもできる.次に例を示します.
auto getValue(int x) -> decltype(x) { return x; } // int

このように書くのはやはり面倒で、C++14の中で簡略化しました.以下のように簡単に書くことができます.
auto getValue(int x) { return x; } // int

しかし、この2つの関数の書き方はあまり役に立たないと思います.ヘッダファイルで関数宣言を書くことができません.
auto getValue(int x);

関数体がないので、タイプの導出はできません...
C++14では、以下の書き方もサポートされています.
decltype(auto) = 3; // int&&
decltype(auto) getRef(int& x) { return x; } // int&

これらの書き方は知っていればいいのに,あまり役に立たない.
Lambda関数
Lambda関数の概念は最初はLisp言語に由来するはずだったが,現在もC++11に吸収されている.Lambda関数を使用すると、変数を定義するように関数を定義できます.たとえば、次のようになります.
auto sum = [](int x, int y) -> int {return x + y;};
cout << sum(2, 3);

上の関数は、次のように簡単に書くこともできます.
auto sum = [](int x, int y) { return x + y; };

コンパイラは、戻り値のタイプを自動的に導出します.
C++14 Lambda関数になるとさらに汎用性がサポートされる.
auto sum = [](auto x, auto y) {return x + y;};  
cout << sum(2, 3) << endl;
cout << sum(2.2, 3.0) << endl;
cout << sum(2.2, 3) << endl;

Lambda関数は、関数のパラメータとして関数に渡すこともできます.次に例を示します.
#include <iostream>
#include <functional>
using namespace std;
void call(int arg, function<void(int)> func) 
{
    func(arg);
}
int main() 
{
    auto printSquare = [](int x) { cout << x*x; };
    call(2, printSquare); // "4"
}

上の例ではLambda関数が一般的な関数ではないことも示しています.特殊なタイプのオブジェクトです.たとえば、次のLambda関数です.
auto sum = [](int x, int y) {return x + y;};  

完全に書きました.
function<int(int)> sum = [](int x, int y) {return x + y;};  

Lambda関数にはいくつかの高度な使い方があります.たとえばLambda関数の「[]」は役に立ちます.これを使用すると、ドメインの他の変数を関数に導入できます.たとえば、次の例です.
void call(function<void()> func) { func(); }
int main() 
{
    int i = 2;
    auto printSquare = [i]() { cout << i*i; };
    call(printSquare); // "4"
}

Lambda関数の[]に値を入力したパラメータは読み取り専用です.次のコードは間違っています
int a = 10;
int b = [a](int i) { a++; return a * i; } (5); // 50

aが読み取り専用ではないようにmutableキーワードを追加することができますが、a値の変更は関数の外に影響しません.
int a = 10;
int b = [a](int i) mutable { a++; return a * i; } (5);
cout << b << endl; // 55
cout << a << endl; // 10

Lambda関数は無名の関数であってもよく,この関数を定義すると同時に呼び出す.そうでなければ、この関数には名前がないので、後で呼び出す機会がありません.次の2つの書き方の結果は同じです.
cout << [](int i) { return i*i; } (101) << endl;

auto printSquare = [](int i) { return i*i; };
cout << printSquare(101) << endl;

「[]」を利用して結果を伝えることもできます.次の2つの方法の結果も同じです.
int a = [](int i) { return i * i; } (11);
cout << a << endl;

[&a] (int i){ a = i * i;}(12);
cout << a << endl;

C++14は、以下のような新しい機能もサポートしています.
int a = 1;
[&, b = 2]() { a += b; }();
cout << a; // "3"

Lambda関数については、これらを知っているとそれほど悪くありません.
第1編は先にこんなにたくさん書きました.次はクラスとオブジェクトの新しい特性について書きます.