「C++の変更点」の版間の差分

提供:MochiuWiki - SUSE, Electronic Circuit, PCB
ナビゲーションに移動 検索に移動
(文字列「<source lang」を「<syntaxhighlight lang」に置換)
14行目: 14行目:
===== クラスの初期化 =====
===== クラスの初期化 =====
C++11では、以下のようにクラスの初期化を定義できる。
C++11では、以下のようにクラスの初期化を定義できる。
  <source lang="c++">
  <syntaxhighlight lang="c++">
  #include <iostream>
  #include <iostream>
   
   
38行目: 38行目:
クラスでも初期化リストを扱えるように、std::initializer_listクラスガ追加された。<br>
クラスでも初期化リストを扱えるように、std::initializer_listクラスガ追加された。<br>
従来では、std::vectorクラスは、初期化リストを指定できず、push_back()で初期化するなどのコーディングを必要としていた。<br>
従来では、std::vectorクラスは、初期化リストを指定できず、push_back()で初期化するなどのコーディングを必要としていた。<br>
  <source lang="c++">
  <syntaxhighlight lang="c++">
  #include <vector>
  #include <vector>
   
   
50行目: 50行目:
<br>
<br>
std::mapクラスであれば、std::make_pair使用して更に面倒なコーディングが必要だったが、C++11では、以下のように記述できる。<br>
std::mapクラスであれば、std::make_pair使用して更に面倒なコーディングが必要だったが、C++11では、以下のように記述できる。<br>
  <source lang="c++">
  <syntaxhighlight lang="c++">
  #include <map>
  #include <map>
  #include <string>
  #include <string>
65行目: 65行目:
<br>
<br>
===== x.begin() / x.end()ではなくbegin(x) / end(x)を使用する =====
===== x.begin() / x.end()ではなくbegin(x) / end(x)を使用する =====
  <source lang="c++">
  <syntaxhighlight lang="c++">
  #include <vector>
  #include <vector>
  #include <algorithm>
  #include <algorithm>
87行目: 87行目:
===== Range-Based Loop =====
===== Range-Based Loop =====
全要素に順次アクセスする場合は、Range-Based Loopを使用する。<br>
全要素に順次アクセスする場合は、Range-Based Loopを使用する。<br>
  <source lang="c++">
  <syntaxhighlight lang="c++">
  // C++98, C++03の記述方法
  // C++98, C++03の記述方法
  for(std::vector<double>::iterator i = dvec.begin(); i != dvec.end(); i++)
  for(std::vector<double>::iterator i = dvec.begin(); i != dvec.end(); i++)
103行目: 103行目:
===== constexpr =====
===== constexpr =====
constexprを使用することで、以下のような初期化ができる。<br>
constexprを使用することで、以下のような初期化ができる。<br>
  <source lang="c++">
  <syntaxhighlight lang="c++">
  #include <iostream>
  #include <iostream>
   
   
123行目: 123行目:
従来のnew / deleteではなく、スマートポインタを使用する。<br>
従来のnew / deleteではなく、スマートポインタを使用する。<br>
詳細は、[[スマートポインタの使い方(unique ptr)]]、[[スマートポインタの使い方(shared ptr)]]、[[スマートポインタの使い方(weak ptr)]]を参照する。<br>
詳細は、[[スマートポインタの使い方(unique ptr)]]、[[スマートポインタの使い方(shared ptr)]]、[[スマートポインタの使い方(weak ptr)]]を参照する。<br>
  <source lang="c++">
  <syntaxhighlight lang="c++">
  #include <memory>
  #include <memory>
   
   
133行目: 133行目:
===== nullptr =====
===== nullptr =====
NULLの定義が0や0Lのため、オーバーロード時の評価で期待した結果が得られない問題あり、nullptrが導入された。<br>
NULLの定義が0や0Lのため、オーバーロード時の評価で期待した結果が得られない問題あり、nullptrが導入された。<br>
  <source lang="c++">
  <syntaxhighlight lang="c++">
  #include <iostream>
  #include <iostream>
   
   
179行目: 179行目:
===== 2進数リテラル =====
===== 2進数リテラル =====
0bもしくは0Bのプレフィックスをつけて、数値の2進数リテラルを記述できる。<br>
0bもしくは0Bのプレフィックスをつけて、数値の2進数リテラルを記述できる。<br>
  <source lang="c++">
  <syntaxhighlight lang="c++">
  int i = 0b1100;  // i = 12
  int i = 0b1100;  // i = 12
  </source>
  </source>
185行目: 185行目:
===== 実行時サイズの配列 =====
===== 実行時サイズの配列 =====
配列のサイズ(要素数)を実行時の値で指定できるようになる。<br>
配列のサイズ(要素数)を実行時の値で指定できるようになる。<br>
  <source lang="c++">
  <syntaxhighlight lang="c++">
  void f(std::size_t size)
  void f(std::size_t size)
  {
  {
196行目: 196行目:
構造体内の動的配列は、Variable Length Array In Structure(VLAIS)と呼ばれる。<br>
構造体内の動的配列は、Variable Length Array In Structure(VLAIS)と呼ばれる。<br>
C++14の実行時サイズ配列は、VLAISをサポートしない。<br>
C++14の実行時サイズ配列は、VLAISをサポートしない。<br>
  <source lang="c++">
  <syntaxhighlight lang="c++">
  void f(std::size_t size)
  void f(std::size_t size)
  {
  {
210行目: 210行目:
ラムダ式と同様に、通常の関数でもreturn文から戻り値の型を推論できる。<br>
ラムダ式と同様に、通常の関数でもreturn文から戻り値の型を推論できる。<br>
return文のオペランドの式から推定される。<br>
return文のオペランドの式から推定される。<br>
  <source lang="c++">
  <syntaxhighlight lang="c++">
  auto f();              // 関数宣言では、戻り値の型は不明
  auto f();              // 関数宣言では、戻り値の型は不明
  auto f(){return 123;};  // 関数f()の定義で、戻り値の型はintとなる。
  auto f(){return 123;};  // 関数f()の定義で、戻り値の型はintとなる。
219行目: 219行目:
decltype(auto)は、戻り値型の推定に追加された機能である。<br>
decltype(auto)は、戻り値型の推定に追加された機能である。<br>
decltype(変数)で、変数の型と同じ型を指定できる。<br>
decltype(変数)で、変数の型と同じ型を指定できる。<br>
  <source lang="c++">
  <syntaxhighlight lang="c++">
  auto a = 10;                // int a = 10; と同義
  auto a = 10;                // int a = 10; と同義
  decltype(a) b;             // int b; と同義
  decltype(a) b;             // int b; と同義
227行目: 227行目:
===== ジェネリックラムダ =====
===== ジェネリックラムダ =====
ラムダ式のパラメータがジェネリックにできる。<br>
ラムダ式のパラメータがジェネリックにできる。<br>
  <source lang="c++">
  <syntaxhighlight lang="c++">
  [](const auto& a, const auto& b){return a > b;};
  [](const auto& a, const auto& b){return a > b;};
  </source>
  </source>
<br>
<br>
また、Varadic template parameter(パラメーターパック)も使用できる。<br>
また、Varadic template parameter(パラメーターパック)も使用できる。<br>
  <source lang="c++">
  <syntaxhighlight lang="c++">
  auto f = [](auto ... args) {};
  auto f = [](auto ... args) {};
  f();
  f();
242行目: 242行目:
===== 一般化されたラムダキャプチャ =====
===== 一般化されたラムダキャプチャ =====
非staticデータメンバーにおいて、コピーキャプチャできる。<br>
非staticデータメンバーにおいて、コピーキャプチャできる。<br>
  <source lang="c++">
  <syntaxhighlight lang="c++">
  int x = 1;
  int x = 1;
  auto f = [y=x, &z=x] { ... };        // xをコピーしたy, xの参照をキャプチャしたz
  auto f = [y=x, &z=x] { ... };        // xをコピーしたy, xの参照をキャプチャしたz
249行目: 249行目:
<br>
<br>
ムーブキャプチャも同様にできる。<br>
ムーブキャプチャも同様にできる。<br>
  <source lang="c++">
  <syntaxhighlight lang="c++">
  std::unique_ptr<int> p(new int(3));
  std::unique_ptr<int> p(new int(3));
  auto f = [q = std::move(p)]{/* 何か処理 */;};
  auto f = [q = std::move(p)]{/* 何か処理 */;};
262行目: 262行目:
*: static, thread_local は除く
*: static, thread_local は除く
* 変数書き換えの許可
* 変数書き換えの許可
  <source lang="c++">
  <syntaxhighlight lang="c++">
  constexpr int abs(int i)
  constexpr int abs(int i)
  {
  {
277行目: 277行目:
変数定義にテンプレートを使用できる。<br>
変数定義にテンプレートを使用できる。<br>
詳細については、[https://kaworu.jpn.org/cpp/変数テンプレート 変数テンプレート(Variable Templates)を参照]すること。<br>
詳細については、[https://kaworu.jpn.org/cpp/変数テンプレート 変数テンプレート(Variable Templates)を参照]すること。<br>
  <source lang="c++">
  <syntaxhighlight lang="c++">
  template <class T> constexpr T pi = T(3.1415);
  template <class T> constexpr T pi = T(3.1415);
   
   
294行目: 294行目:
数値区切りとは、数値リテラルを単一引用符で区切る機能である。<br>
数値区切りとは、数値リテラルを単一引用符で区切る機能である。<br>
区切りの桁は任意であり、大きな数字は、区切りを入れたほうが「人に解りやすい」というメリットがある。<br>
区切りの桁は任意であり、大きな数字は、区切りを入れたほうが「人に解りやすい」というメリットがある。<br>
  <source lang="c++">
  <syntaxhighlight lang="c++">
  int a = 1'000;                   // 千
  int a = 1'000;                   // 千
  long int b = 1'000'000;          // 100万
  long int b = 1'000'000;          // 100万
301行目: 301行目:
<br>
<br>
2進数や16進数も区切ると見やすくなる。<br>
2進数や16進数も区切ると見やすくなる。<br>
  <source lang="c++">
  <syntaxhighlight lang="c++">
  uint16_t a = 0b11110000'00001111;  // 1[byte]ずつ区切る
  uint16_t a = 0b11110000'00001111;  // 1[byte]ずつ区切る
  uint32_t b = 0xff'00'ff'ff;        // 1[byte]ずつ区切る
  uint32_t b = 0xff'00'ff'ff;        // 1[byte]ずつ区切る
307行目: 307行目:
<br>
<br>
小数点を区切ることもできる。<br>
小数点を区切ることもできる。<br>
  <source lang="c++">
  <syntaxhighlight lang="c++">
  double pi = 3.14159'26535;
  double pi = 3.14159'26535;
  </source>
  </source>
314行目: 314行目:
deprecated属性は、エンティティを非推奨扱いする属性である。<br>
deprecated属性は、エンティティを非推奨扱いする属性である。<br>
「非推奨扱いの名前」が使用される場合、「警告メッセージ」を出力させることができる。<br>
「非推奨扱いの名前」が使用される場合、「警告メッセージ」を出力させることができる。<br>
  <source lang="c++">
  <syntaxhighlight lang="c++">
  // 非推奨の関数
  // 非推奨の関数
  [[deprecated]] char * gets(char *str);
  [[deprecated]] char * gets(char *str);

2021年11月24日 (水) 08:29時点における版

概要

ここでは、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では、以下のようにクラスの初期化を定義できる。

<syntaxhighlight lang="c++">
#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;
}
</source>


コンテナクラスの初期化

クラスでも初期化リストを扱えるように、std::initializer_listクラスガ追加された。
従来では、std::vectorクラスは、初期化リストを指定できず、push_back()で初期化するなどのコーディングを必要としていた。

<syntaxhighlight lang="c++">
#include <vector>

int main(int argc, const char *argv[])
{
   std::vector<int> v = {1,2,3};

   return 0;
}
</source>


std::mapクラスであれば、std::make_pair使用して更に面倒なコーディングが必要だったが、C++11では、以下のように記述できる。

<syntaxhighlight lang="c++">
#include <map>
#include <string>

int main(int argc, const char *argv[])
{
   std::map<string, int> m = {
                                {"foo",1},
                                {"bar",2},
                             };
   return 0;
}
</source>


x.begin() / x.end()ではなくbegin(x) / end(x)を使用する
<syntaxhighlight lang="c++">
#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));
</source>


推論型

変数の型を推論型にすることで、プログラミングが簡単・簡素になる。
autoとRange-Looped forを組み合わせることでループが簡単に記述できる。

Range-Based Loop

全要素に順次アクセスする場合は、Range-Based Loopを使用する。

<syntaxhighlight lang="c++">
// 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;
}
</source>


constexpr

constexprを使用することで、以下のような初期化ができる。

<syntaxhighlight lang="c++">
#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;
}
</source>


スマートポインタ

従来のnew / deleteではなく、スマートポインタを使用する。
詳細は、スマートポインタの使い方(unique ptr)スマートポインタの使い方(shared ptr)スマートポインタの使い方(weak ptr)を参照する。

<syntaxhighlight lang="c++">
#include <memory>

std::unique_ptr<int> pihoge(new(10));
std::shared_ptr<std::string> pstrhoge(new(""));
std::weak_ptr<double> pdhoge(new(5.0f));
</source>


nullptr

NULLの定義が0や0Lのため、オーバーロード時の評価で期待した結果が得られない問題あり、nullptrが導入された。

<syntaxhighlight lang="c++">
#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;
}
</source>



C++14の変更点

C++14とは、C++11のマイナーバージョンアップとラムダ式などの機能追加を行われたC++である。
この機能を使用するためには、C++14に対応したコンパイラ(g++、clang++等)が必要である。

  • 2進数リテラル
  • 実行時サイズの配列
  • 通常の関数の戻り値型の推論
  • ジェネリックラムダ
  • 一般化されたラムダキャプチャ
  • constexpr関数の制限の緩和
  • 変数テンプレート
  • 軽量コンセプト


2進数リテラル

0bもしくは0Bのプレフィックスをつけて、数値の2進数リテラルを記述できる。

<syntaxhighlight lang="c++">
int i = 0b1100;  // i = 12
</source>


実行時サイズの配列

配列のサイズ(要素数)を実行時の値で指定できるようになる。

<syntaxhighlight lang="c++">
void f(std::size_t size)
{
   int a[size];  // 実行時サイズの配列
}
</source>


C言語とは、sizeofで値が取得できない等、細かい部分で互換性がない。
また、クラス内で使用できない場合がある。
構造体内の動的配列は、Variable Length Array In Structure(VLAIS)と呼ばれる。
C++14の実行時サイズ配列は、VLAISをサポートしない。

<syntaxhighlight lang="c++">
void f(std::size_t size)
{
   struct
   {
      int a[size]; // GCC VLAIS, C++14 ではエラーになる
   } valis;
}
</source>


通常の関数の戻り値型の推論

ラムダ式と同様に、通常の関数でもreturn文から戻り値の型を推論できる。
return文のオペランドの式から推定される。

<syntaxhighlight lang="c++">
auto f();               // 関数宣言では、戻り値の型は不明
auto f(){return 123;};  // 関数f()の定義で、戻り値の型はintとなる。
int x = f();            // x = 123
</source>


decltype(auto)

decltype(auto)は、戻り値型の推定に追加された機能である。
decltype(変数)で、変数の型と同じ型を指定できる。

<syntaxhighlight lang="c++">
auto a = 10;                // int a = 10; と同義
decltype(a) b;	             // int b; と同義
std::vector<decltype<a> v;  // std::vector<int> v; と同義
</source>


ジェネリックラムダ

ラムダ式のパラメータがジェネリックにできる。

<syntaxhighlight lang="c++">
[](const auto& a, const auto& b){return a > b;};
</source>


また、Varadic template parameter(パラメーターパック)も使用できる。

<syntaxhighlight lang="c++">
auto f = [](auto ... args) {};
f();
f(0);
f(0,1);
f(0,1,2,3,4,5);
</source>


一般化されたラムダキャプチャ

非staticデータメンバーにおいて、コピーキャプチャできる。

<syntaxhighlight lang="c++">
int x = 1;
auto f = [y=x, &z=x] { ... };        // xをコピーしたy, xの参照をキャプチャしたz
auto g = [y = x + 1] { return y; };  // 2を返す
</source>


ムーブキャプチャも同様にできる。

<syntaxhighlight lang="c++">
std::unique_ptr<int> p(new int(3));
auto f = [q = std::move(p)]{/* 何か処理 */;};
</source>


constexpr関数の制限の緩和
  • if, switch による条件分岐を許可
  • for, while, do-while のループの許可
  • void 戻り値型の許可
    パラメータの参照で書き換えを行う
  • 初期化をともなう変数宣言の許可
    static, thread_local は除く
  • 変数書き換えの許可
<syntaxhighlight lang="c++">
constexpr int abs(int i)
{
   if (i < 0)
   {
      i = -i;
   }

   return i;
}
</source>


変数テンプレート

変数定義にテンプレートを使用できる。
詳細については、変数テンプレート(Variable Templates)を参照すること。

<syntaxhighlight lang="c++">
template <class T> constexpr T pi = T(3.1415);

template <class T>
T menseki(T hankei)
{
   return pi<T> * hankei * hankei;
}
</source>


軽量コンセプト
  • コンセプトの軽量版
  • テンプレートの型制約機能


数値区切り

数値区切りとは、数値リテラルを単一引用符で区切る機能である。
区切りの桁は任意であり、大きな数字は、区切りを入れたほうが「人に解りやすい」というメリットがある。

<syntaxhighlight lang="c++">
int a = 1'000;	                   // 千
long int b = 1'000'000;           // 100万
long long int c = 1'000'000'000;  // 10億
</source>


2進数や16進数も区切ると見やすくなる。

<syntaxhighlight lang="c++">
uint16_t a = 0b11110000'00001111;  // 1[byte]ずつ区切る
uint32_t b = 0xff'00'ff'ff;        // 1[byte]ずつ区切る
</source>


小数点を区切ることもできる。

<syntaxhighlight lang="c++">
double pi = 3.14159'26535;
</source>


非推奨をマークする (deprecated)

deprecated属性は、エンティティを非推奨扱いする属性である。
「非推奨扱いの名前」が使用される場合、「警告メッセージ」を出力させることができる。

<syntaxhighlight lang="c++">
// 非推奨の関数
deprecated char * gets(char *str);

// コメント
deprecated("gets is deprecated. Use gets_s instead.")
</source>


例えば、「非推奨の関数」はいきなり削除はできない。
現実的な解として、「非推奨の関数」の実装を置き換えることが可能かもしれないが、必ずしも置き換え可能ではなく、完全な形の置き換えができるかは不明である。
なぜなら、実装を変更することにより、互換性の問題が発生する等、サイドエフェクトが考えられるからである。
「非推奨な関数」を利用しているかどうか気付くのは難しく、C++コンパイラで警告を出し、プログラマに解決して貰う方が良い。