「C++の変更点」の版間の差分
細 (文字列「</source>」を「</syntaxhighlight>」に置換) |
|||
122行目: | 122行目: | ||
===== スマートポインタ ===== | ===== スマートポインタ ===== | ||
従来のnew / deleteではなく、スマートポインタを使用する。<br> | 従来のnew / deleteではなく、スマートポインタを使用する。<br> | ||
詳細は、[[ | 詳細は、[[C++の基礎 - スマートポインタ(unique ptr)]]、[[C++の基礎 - スマートポインタ(shared ptr)]]、[[C++の基礎 - スマートポインタ(weak ptr)]]を参照する。<br> | ||
<syntaxhighlight lang="c++"> | <syntaxhighlight lang="c++"> | ||
#include <memory> | #include <memory> | ||
131行目: | 131行目: | ||
</syntaxhighlight> | </syntaxhighlight> | ||
<br> | <br> | ||
===== nullptr ===== | ===== nullptr ===== | ||
NULLの定義が0や0Lのため、オーバーロード時の評価で期待した結果が得られない問題あり、nullptrが導入された。<br> | NULLの定義が0や0Lのため、オーバーロード時の評価で期待した結果が得られない問題あり、nullptrが導入された。<br> |
2023年2月19日 (日) 07:40時点における最新版
概要
ここでは、C++11以降の変更点を記載する。
C++11の変更点
C++11の拡張やライブラリを使用するためには、C++11に対応したコンパイラが必要になる。
また、コンパイラによっては、C++11の機能を使用するために、明示的にオプションで指定する必要がある。
以下は、コンパイルオプションの例である。
g++49 -std=c++11 foo.cc g++48 -std=c++11 foo.cc clang++ -std=c++11 foo.cc
クラスの初期化
C++11では、以下のようにクラスの初期化を定義できる。
#include <iostream>
class C
{
private:
int i;
public:
C (int ia) : i(ia) {} // ユーザ定義のコンストラクタ
C () : C (123) {} // デフォルトコンストラクタ
};
int main(int argc, const char *argv[])
{
C c;
return 0;
}
コンテナクラスの初期化
クラスでも初期化リストを扱えるように、std::initializer_listクラスガ追加された。
従来では、std::vectorクラスは、初期化リストを指定できず、push_back()で初期化するなどのコーディングを必要としていた。
#include <vector>
int main(int argc, const char *argv[])
{
std::vector<int> v = {1,2,3};
return 0;
}
std::mapクラスであれば、std::make_pair使用して更に面倒なコーディングが必要だったが、C++11では、以下のように記述できる。
#include <map>
#include <string>
int main(int argc, const char *argv[])
{
std::map<string, int> m = {
{"foo",1},
{"bar",2},
};
return 0;
}
x.begin() / x.end()ではなくbegin(x) / end(x)を使用する
#include <vector>
#include <algorithm>
int iarray[] = {9, 7 ,8, 1, 5};
std::vector<int> ivec = {5, 4, 0, 7, 1};
// C++98, C++03の記述方法
std::sort(&iarray[0], &iarray[0] + sizeof(a) / sizeof(a[0]));
std::sort(ivec.begin(), ivec.end());
// C++11の記述方法
std::sort(begin(iarray), end(iarray));
std::sort(begin(ivec), end(ivec));
推論型
変数の型を推論型にすることで、プログラミングが簡単・簡素になる。
autoとRange-Looped forを組み合わせることでループが簡単に記述できる。
Range-Based Loop
全要素に順次アクセスする場合は、Range-Based Loopを使用する。
// C++98, C++03の記述方法
for(std::vector<double>::iterator i = dvec.begin(); i != dvec.end(); i++)
{
sum += *i;
}
// C++11の記述方法
for(auto d : dvec)
{
total += d;
}
constexpr
constexprを使用することで、以下のような初期化ができる。
#include <iostream>
typedef struct tag_X
{
int n;
} X
int main(int argc, const char *argv[])
{
constexpr X x = {123};
int a[x.n] = {1};
return 0;
}
スマートポインタ
従来のnew / deleteではなく、スマートポインタを使用する。
詳細は、C++の基礎 - スマートポインタ(unique ptr)、C++の基礎 - スマートポインタ(shared ptr)、C++の基礎 - スマートポインタ(weak ptr)を参照する。
#include <memory>
std::unique_ptr<int> pihoge(new(10));
std::shared_ptr<std::string> pstrhoge(new(""));
std::weak_ptr<double> pdhoge(new(5.0f));
nullptr
NULLの定義が0や0Lのため、オーバーロード時の評価で期待した結果が得られない問題あり、nullptrが導入された。
#include <iostream>
void f(double *fptr)
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
void f(int i)
{
std::cout << __PRETTY_FUNCTION__ << std::endl;
}
int main(int argc, const char *argv[])
{
f(0); // f(int)
f(nullptr); // f(double*)
{
char *phoge1 = NULL;
char *phoge2 = nullptr;
if (phoge1 == phoge2)
{
}
}
return 0;
}
C++14の変更点
C++14とは、C++11のマイナーバージョンアップとラムダ式などの機能追加を行われたC++である。
この機能を使用するためには、C++14に対応したコンパイラ(g++、clang++等)が必要である。
- 2進数リテラル
- 実行時サイズの配列
- 通常の関数の戻り値型の推論
- ジェネリックラムダ
- 一般化されたラムダキャプチャ
- constexpr関数の制限の緩和
- 変数テンプレート
- 軽量コンセプト
2進数リテラル
0bもしくは0Bのプレフィックスをつけて、数値の2進数リテラルを記述できる。
int i = 0b1100; // i = 12
実行時サイズの配列
配列のサイズ(要素数)を実行時の値で指定できるようになる。
void f(std::size_t size)
{
int a[size]; // 実行時サイズの配列
}
C言語とは、sizeofで値が取得できない等、細かい部分で互換性がない。
また、クラス内で使用できない場合がある。
構造体内の動的配列は、Variable Length Array In Structure(VLAIS)と呼ばれる。
C++14の実行時サイズ配列は、VLAISをサポートしない。
void f(std::size_t size)
{
struct
{
int a[size]; // GCC VLAIS, C++14 ではエラーになる
} valis;
}
通常の関数の戻り値型の推論
ラムダ式と同様に、通常の関数でもreturn文から戻り値の型を推論できる。
return文のオペランドの式から推定される。
auto f(); // 関数宣言では、戻り値の型は不明
auto f(){return 123;}; // 関数f()の定義で、戻り値の型はintとなる。
int x = f(); // x = 123
decltype(auto)
decltype(auto)は、戻り値型の推定に追加された機能である。
decltype(変数)で、変数の型と同じ型を指定できる。
auto a = 10; // int a = 10; と同義
decltype(a) b; // int b; と同義
std::vector<decltype<a> v; // std::vector<int> v; と同義
ジェネリックラムダ
ラムダ式のパラメータがジェネリックにできる。
[](const auto& a, const auto& b){return a > b;};
また、Varadic template parameter(パラメーターパック)も使用できる。
auto f = [](auto ... args) {};
f();
f(0);
f(0,1);
f(0,1,2,3,4,5);
一般化されたラムダキャプチャ
非staticデータメンバーにおいて、コピーキャプチャできる。
int x = 1;
auto f = [y=x, &z=x] { ... }; // xをコピーしたy, xの参照をキャプチャしたz
auto g = [y = x + 1] { return y; }; // 2を返す
ムーブキャプチャも同様にできる。
std::unique_ptr<int> p(new int(3));
auto f = [q = std::move(p)]{/* 何か処理 */;};
constexpr関数の制限の緩和
- if, switch による条件分岐を許可
- for, while, do-while のループの許可
- void 戻り値型の許可
- パラメータの参照で書き換えを行う
- 初期化をともなう変数宣言の許可
- static, thread_local は除く
- 変数書き換えの許可
constexpr int abs(int i)
{
if (i < 0)
{
i = -i;
}
return i;
}
変数テンプレート
変数定義にテンプレートを使用できる。
詳細については、変数テンプレート(Variable Templates)を参照すること。
template <class T> constexpr T pi = T(3.1415);
template <class T>
T menseki(T hankei)
{
return pi<T> * hankei * hankei;
}
軽量コンセプト
- コンセプトの軽量版
- テンプレートの型制約機能
数値区切り
数値区切りとは、数値リテラルを単一引用符で区切る機能である。
区切りの桁は任意であり、大きな数字は、区切りを入れたほうが「人に解りやすい」というメリットがある。
int a = 1'000; // 千
long int b = 1'000'000; // 100万
long long int c = 1'000'000'000; // 10億
2進数や16進数も区切ると見やすくなる。
uint16_t a = 0b11110000'00001111; // 1[byte]ずつ区切る
uint32_t b = 0xff'00'ff'ff; // 1[byte]ずつ区切る
小数点を区切ることもできる。
double pi = 3.14159'26535;
非推奨をマークする (deprecated)
deprecated属性は、エンティティを非推奨扱いする属性である。
「非推奨扱いの名前」が使用される場合、「警告メッセージ」を出力させることができる。
// 非推奨の関数
[[deprecated]] char * gets(char *str);
// コメント
[[deprecated("gets is deprecated. Use gets_s instead.")]]
例えば、「非推奨の関数」はいきなり削除はできない。
現実的な解として、「非推奨の関数」の実装を置き換えることが可能かもしれないが、必ずしも置き換え可能ではなく、完全な形の置き換えができるかは不明である。
なぜなら、実装を変更することにより、互換性の問題が発生する等、サイドエフェクトが考えられるからである。
「非推奨な関数」を利用しているかどうか気付くのは難しく、C++コンパイラで警告を出し、プログラマに解決して貰う方が良い。