TNGとは?

この記事は今後、大きく加筆・修正される予定です!!

(中の人は学祭当日未明、この記事を書いています…

 

「TNG」はTNPのゲームを一括管理・公開するために作られたランチャーのような何かです。同じ役割を担っていた、先代の「ゲームジェネレーター」の改良版として、一年前から制作されてきました。(ただし今もバグまみれの模様)

制作は私サバと、現会長のコショウ氏が主に行い、アイコンは蟹氏に描いていただきました。イメージは魚のタナゴだそうです。(TNG+魚要素=タナゴ)

ちなみにTNGは「Tnp’s Next generation Game launcher」の略です。さらに展開すると、「The next generation programmer’s Next generation Game launcher」という php のような再帰的頭字語みがある気がして気に入ってます。

 

C++が使えたい 項目7(現行のC++への移行 – 1)

サバです。
3章にはいります!

今回も、表とその下の解説は無駄に長いので、適当に流し読みしてください!

 

 

<以下長い注意>
ここの内容は、サバが適当な自己解釈を重ねた結果、攪拌され白濁してしまった知識の泉から、沈殿物を素手で持ち上げようとした結果です。保証はしかねます。ご了承ください。

内容はEffectiveModernC++をわざわざ書き写したような何かです。
題名にEffectiveModernC++が入っていないのは更新が途中で止まる自信があるからです。

また、もしよろしければ、間違い、気になったところ、分かりづらいところなどを指摘してもらえると、サバがピチピチ跳ねて喜びます。喜びすぎてプログラミング言語C++第四版をあなたに投げつけるかもしれません。
(ぜひ教えてください。よろしくお願いします。 < ( _ _ ) > 何でもするとは言いませんから…

 

 

 

項目7 オブジェクト作成時の()と{}の違い

結論:{}を使うならstd::initializer_listコンストラクタに気をつける

 

C++11はオブジェクト作成時の初期化に(){}を使い分けられます。ただし、組み込み型(プリミティブ型)変数の初期化には = も使えるため、初期化代入がごっちゃに理解されることが多いようです。自分もそうでした。

 

 

 

 

・初期化方法まとめ

重要なのは組み込み型の = 初期化特殊であることです。
ここではユーザ定義型(構造体・クラス)の例として Widget という型名を用います。

組み込み型
(クラス・構造体以外)
int Integer1 = 0;
int Integer2( 0 );
int Integer3{ 0 };
すべて同じ意味
0 で int 型変数を作成・初期化
= でも初期化をすることができる
初期化であって代入ではない
ユーザ定義型
(クラス・構造体)
Widget widget1( 0 );
Widget widget2{ 0 };
すべて同じ意味
Widgetのコンストラクタに 0 を渡して、Widgetオブジェクトを作成・初期化
  • ここにでは省略しましたが、{} の代わりに = {} も使用できます。全く同じ意味です。
  • ちなみにユーザ定義型で、下コード widget5 のように初期化をしない場合、デフォルトコンストラクタによって暗黙的に初期化されます。個人的には初期化を忘れたバグの根源なのか、デフォルト初期化したいのかが分からないので、widget6 の明示的初期化が良いと思います。
    Widget widget5;
    Widget widget6{};
    どちらもデフォルトコンストラクタで初期化

  • コンパイラによっては1変数を受け取るユーザ定義型のコンストラクタなら = でも初期化できるようです。

 

 

 

 

 

・初期化の統一記法

C++は変態なので変数を初期化する場所・場合がたくさんあります。
そんな中でも {} だけは統一的にすべての初期化を行うことができます。{}すんごい!!

組み込み型int integer1 = 0;
int integer2();
int integer3{};
= () {} 全てで初期化できます。
全てデフォルト値0での初期化です。
ユーザ定義型 Widget widget1();
Widget widget2{};

// NG : 二度手間
Widget widget3 = Widget{};

= では期待通りの初期化はできません。一時オブジェクトを作成し、ムーブコンストラクタでムーブ初期化するという二度手間になります。
(最適化されなければ)
宣言初期化子 class Widget
{
    int member1 = 0;
    int member2{};

    // メソッド?
    int member3();

};
() では初期化できません。
コンパイラにとってはメソッド
(メンバ関数)宣言にしか見えません。
コンストラクタ初期化子class Widget
{
    int member1;
    int member2;

public:  
    Widget(int value)
        : member1{ value }
        , member2( value )

    {}
};

= では初期化できません。
メンバを初期化するためのもので、代入するためのものではありません。(適当に理由を付けました)
コピーできないオブジェクトstd::atomic<int> ai{ 0 };

// Error : 関数は削除されてる
std::atomic<int> ai =
        std::atomic<int>{ 0 };

コピーやムーブを出来ないように設計されたオブジェクトは = 等を使えません。

左の例ではムーブコンストラクタが消されているため、エラーとなります。

template
との組み合わせ
template<typename T> decltype(auto) func()
{
    // どっち?
    return T::Whats();
}
こんな状況で () 初期化を使うのはやめましょう。Whats という static 関数呼び出しにしか見えません。
このコードで Whats オブジェクトの初期化をしていた時は、
バグの発見に大分骨を折りそうです。
  • 上の例を見ると {} だけいつでも初期化に使えることが分かります。

 

 

 

 

 

 

・ {} 初期化の型チェック機能

{} 初期化は統一記法以外にも型チェックという利点があります。
縮小変換(精度が落ちる変換)が必要な初期化が行われた場合、コンパイラがエラーを出してくれます。(コンパイラによっては警告)

int  pi { 3.14 };              // Error

double
  x{}, y{} z{};
int  sum { x + y + z };    // Error
 double 型から int 型への暗黙的な縮小変換
 (精度が落ちる・値が壊れるような型変換)
bool flag { 2 };               // Errorint 型から bool 型への暗黙的な縮小変換
  • エラーを避けるためには static_cast を使いましょう
    int pi{ static_cast<int>( 3.14 ) };

 

 

 

 

 

・{} 初期化の落とし穴

{} は凄い!ですが、致命的な問題があります。
それは std::initializer_list との相性問題です。
std::initializer_list を取るコンストラクタを持つオブジェクトを {} 初期化すると、縮小変換をしてでも、力ずくでこの std::initializer_list コンストラクタを使おうとします。

class Widget
{

    // コンストラクタ1
    Widget( int value )
    {}
    // コンストラクタ2
    Widget( std::initializer_list<bool> list ) 
    {}

};

int main()
{
    // コンストラクタ2を呼び出す
    Widget w{ 100 } // 縮小変換 Error も

    return 0;
}

main 関数内で、Widget の
コンストラクタ1
を呼び出しているように見えますが、
コンストラクタ2
が呼び出されます。
 

コンストラクタ1 を呼び出すには () を使った初期化をする必要があります。
 

また、{} 初期化は型チェックを行うため、この場合 Error や Warning が発生します。

  • コンパイラには {} が std::initializer_list にしか見えないのでしょうか…
  • 力ずくで変換できない場合、つまり、暗黙の型変換方法がない場合は、こんなことになりません。 例えば、std::initializer_list<bool> ではなく、 std::initializer_list<std::string>であった場合は、int 型から std::string 型への暗黙の型変換方法がないため、コンストラクタ1 が呼び出されます。

 

残念ながらこの相性問題の代表例は STL です。
std::vector も std::initializer_list をとるコンストラクタを持つため、こんな頭痛が痛い問題が起きます。

// { 10, 1 }
std::vector<int> vec1{ 10, 1 };
要素数2で、それぞれの値が 10, 1 となる
std::vector を作成
// { 1, 1, 1, 1, 1, 1, 1, 1, 1, 1 }
std::vector<int> vec2( 10, 1 );
要素数10で、すべての値が 1 となる
std::vector を作成
// { 100 }
std::vector<int> vec3{ 100 };
要素が 100 のみで要素数1となる
std::vector を作成
// { 0, 0, 0, … ( x 100) }
std::vector<int> vec4( 100 );
要素数100で、すべての要素をデフォルト値( 0 )で初期化した std::vector を作成
  • std::vector は {} だけじゃすべての初期化ができない… {} は初期化の統一記法じゃなかった!!!

 

初期化するオブジェクトに std::initializer_list をとるものがないか事前に確認しましょう。std::initializer_list をとるコンストラクタを定義するときは本当に必要なのか考えましょう。

 

 

 

 

 

 

Effective Modern C++ の中では、(), {} どちらの初期化も長所・短所を持つため、いずれかの記法を優先して使うように決め、必要に応じてもう一方を使おうとまとめられています。

 

 

 

 

< 前の項目へ   次の項目へ >  // そのうち…

C++が使えたい 項目6(auto – 2)

サバです。
autoについては最後の項目です(まだ二つ目)

 

<以下長い注意>
ここの内容は、サバが適当な自己解釈を重ねた結果、攪拌され白濁してしまった知識の泉から、沈殿物を素手で持ち上げようとした結果です。保証はしかねます。ご了承ください。

内容はEffectiveModernC++をわざわざ書き写したような何かです。
題名にEffectiveModernC++が入っていないのは更新が途中で止まる自信があるからです。

また、もしよろしければ、間違い、気になったところ、分かりづらいところなどを指摘してもらえると、サバがピチピチ跳ねて喜びます。喜びすぎてプログラミング言語C++第四版をあなたに投げつけるかもしれません。
(ぜひ教えてください。よろしくお願いします。 < ( _ _ ) > 何でもするとは言いませんから…

 

 

項目5 auto が期待とは異なる型を推論する場面では ETII を用いる

 

結論:autoは明示的キャストと併用しよう

ETIIはC++的で明示的な型キャスト、static_cast を使ってauto変数を初期化する方法です。

auto itsInteger = static_cast<int>(64 * 0.2);    // キャストでint型変数として推論させる。
  • これを使ったらauto使えるし、どんな型と目的でキャストしてるのか明示的でわかりやすいよね!って話です。

 

また、std::vector<bool> を使うときにこの操作が必要な場合があります。
というのもstd::vector<bool>の [ ] 演算子は bool& ではない変な型を返してくるからです。

std::vector<int> vecInt = {1, 2, 3};
decltype( veclnt[0] );
auto front = vecInt[0];
decltype → int& // 分かる。内部要素への参照。
front       → int   // 期待通り。
std::vector<bool> vecBool = {true, true};
decltype( vecBool[0] );
auto front = vecBool[0];
decltype → std::vector<bool>::reference // WTF
front       → std::vector<bool>::reference // WTF

うーんこの…。そこでこの std::vector<bool> 使う時だけETIIしよって話です。

std::vector<bool> vecBool = {true, true};
auto front = static_cast<bool>( vecBool[0] )
front → bool   // 期待通り
  • もちろんbool型以外のstd::vectorはこんなことないです。ふつうです。こいつが変態なだけです。
  • めんどい…
  • 今にでもstd::vector<bool>に殴り掛かれそうなら、殴り掛かる前に下の内容を読んでみてください。

 

 

 

・深淵を覗きに行くために…

std::vector<bool>はうん○こ。ではなくちゃんと理由があるみたいです。

bool型のベクターはメモリを節約するため、内部でビット演算を使用して値を保持しています。となると、要素一つあたりのサイズは1ビット。でもC++の参照が指し示すことができるのはバイト単位…(参照は内部的にはポインタであるため)あれ? ビット単位な各要素への参照ってできないじゃん。じゃあ参照受け取って、各要素の値を直接変更するとかもできないじゃん! って当然なるわけです。

そこでstd::vector<bool>::reference君の登場です。下記のコードのように、ぱっと見いい感じに動作するよう彼は実装されています。この書き方だけならいつもの通りです。

std::vector<bool> vecBool = {false, false};
vecBool[0] = true;
vecBool[1] = true;
{false, false} // vecBool の中身
{true, false}
{true, true}

 

しかし、上記の注意点だけでは終わらず、追加でもう一つが紹介されています。
それは[]演算子の戻り値は値渡しでも、実質的には参照渡しであるということです。
例を挙げると、以下のようなコードは未定義動作です。

auto first = std::vector<bool>{false, false}[0];   //参照渡しと実質的に同義な値渡し
first = true;                                                        // 死んだオブジェクトに代入する未定義動作
  • std::vector<bool>::reference 君は、各要素を参照してるっぽく動作するため、ビットとして存在する実態にアクセスするための情報を持っています。実装方法によっては、ポインタとそこから何ビット目かといった感じの情報です。
  • 上記例では、std::vector<bool>::reference を first は受け取ります。そのため first は std::vector<bool>{false, false} と宣言された右辺値の内部を指し示すポインタを持つことになります。
  • 右辺値は一行だけの命です。つぎのセミコロンが来たらそこで死にます。
  • つまりfirst = true と代入している時点で、first 内部のポインタは、すでに死んだvectorが存在していた位置を指し示します。このコードは代入によってメモリ空間をぐちゃぐちゃにできるわけです。
  • このようなバグは、関数の戻り値(値渡し)でももちろん起こりえます。
  • std::vector<bool>怖い…

 

std::vector<bool>::referenceのような、ほかの型の模倣や拡張を目的としたクラスを、プロクシクラス(proxy class)とか言うらしいです。

このようなクラスの存在に気づくためには、ヘッダファイルの実装を自分で見ようねという話みたいです。C++楽じいなぁ…

 

C++は楽しい。

 

 

< 前の項目へ   次の項目へ >  // そのうち…

C++が使えたい 項目5(auto – 1)

サバです。
前回の記事は投稿後に大分変更・加筆をしたので、チラ見してくださいお願いします。
それに加えて、詰め込みすぎたので今回から1項目くらいずつで行きたいと思います。

 

<以下長い注意>
ここの内容は、サバが適当な自己解釈を重ねた結果、攪拌され白濁してしまった知識の泉から、沈殿物を素手で持ち上げようとした結果です。保証はしかねます。ご了承ください。

内容はEffectiveModernC++をわざわざ書き写したような何かです。
題名にEffectiveModernC++が入っていないのは更新が途中で止まる自信があるからです。

また、もしよろしければ、間違い、気になったところ、分かりづらいところなどを指摘してもらえると、サバがピチピチ跳ねて喜びます。喜びすぎてプログラミング言語C++第四版をあなたに投げつけるかもしれません。
(ぜひ教えてください。よろしくお願いします。 < ( _ _ ) > 何でもするとは言いませんから…

 

 

 

項目5 明示的型宣言よりも auto を優先する

 

結論:いいものはどんどん使おう!

IDEあるならとりま使いましょう。ぐう有能。
(もちろん注意するとこもあるみたいですが…

具体的な使用例(特に変数宣言範囲for文!!)

変数宣言auto value = 0; int
範囲for文for (const auto& content : container) { }範囲for文 + auto&は最強
関数の戻り値auto func(){ return “auau”; }const char *
ラムダ式の引数 auto lambda = [](auto& arg)

    std::cout << arg;
};
これはC++14以降の機能

 

 

 

 

・auto ? なにそれおいしいの?

    つ autoはおいちい。(前回の記事)
   もし良ければ下にある「autoの利点」もどうぞ…
     ( このサイトが大好き。 : cpprefjp) // 神様です。いつもありがとうございます。
  

 

 

 

 

・深淵を覗きに行くために…

 auto の利点

 ・変数の初期化忘れがなくなる

    auto i = 0;
  • 型推論のために必ず初期値を指定する必要がある。

 

 ・コンパイラが適切な型を選んでくれる

    std::vector<int> vec;
    auto size = vec.size(); 
  • std::vector<int>のサイズを表す非負整数型は std::vector<int>::size_type だけど、そんな長いの覚えてコーディングする必要がなくなる。

 

 ・労力が減る

    std::vector<int> data(10)

    for (auto& val : data)
        std::cin >> val;

    for (auto ite = data.crbegin(); ite != data.crend(); ++ite)
        std::cout << *ite << std::endl;

  • 入力から10個の整数を受け取って逆順で出力するプログラム
  • std::vector<int> の int を書き換えるだけでどんな型にも対応できる(double, std::string, long, etc…)
  • イテレータの型指定で悩むこともなくなる
  • コンパイラに任せた方がヒューマンエラーは減る。楽しよう!

 

 

私は脳死して使うようにしています。autoだいしゅき。
C++に追加されたということは、いいからだまって使えという機能のはずですから。(盲信)

 

 

< 前の項目へ   次の項目へ >

C++が使えたい 項目1~4(型推論)

サバです。最近EffectiveModernC++をやっと買えたと思ったら、ついでに期末試験が迫ってきてしまったので、現実逃避しながらその内容について書いてきたいと思います。(本の内容を自分解釈で出力しとけば忘れたときに楽かなぁ…とも思ったので!)
この本にとっかかりやすくなってもらえるとうれしいなぁ

 

<注意>
ここの内容は、サバが適当な自己解釈を重ねた結果、攪拌され白濁してしまった知識の泉から、沈殿物を素手で持ち上げようとした結果です。保証はしかねます。ご了承ください。

題名にEffectiveModernC++が入っていないのは更新が途中で止まる自信があるからです。

また、もしよろしければ、間違い、気になったところ、分かりづらいところなどを指摘してもらえると、サバがピチピチ跳ねて喜びます。喜びすぎてプログラミング言語C++第四版をあなたに投げつけるかもしれません。
ぜひ教えてください。よろしくお願いします。 < ( _ _ ) > 何でもするとは言いませんから…

 

 

 

 

1章 型推論

 

項目1 テンプレートの型推論を理解する

 

結論:大体期待通り!

C++の深淵は闇の中だとしても、templateは晴れた日のあなたの部屋くらい明るい。
C++なんて、第一に安全、第二に効率を考えて、Cから派生したってことだけ分かっていれば、もう何も怖くない!!(そう願いたい…)
まさにtemplateはそんな感じ。(希望的観測)

 

template<typename T>  と宣言したとき、Tの使い方は参照について考えると3つあります。

1. void f (T arg) { }      : arg は値渡し

  • const , volatileは完全になかったことにされる

2. void f (T& arg) { }   : arg は参照渡し

  • この関数使うときに右辺値の引数をぶち込むとエラー

3. void f (T&& arg) { } : arg はユニバーサル参照渡し

  • ユニバーサル参照 : 左辺値参照・右辺値参照のどちらもイける両刀使い

 

上記の f を f (value) と呼び出したとき、 Targ  の
推論される型を表に入れるとこうなります。大体…期待通りです。(volatileもconstと同じ)

T + &TT&T&&
TargTargTarg
int value;intintintint&int&int&
int& value;intintintint&int&int&
int&& value;intintintint&int&int&
const int value;intintconst intconst int&const int&const int&
const int& value;intintconst intconst int&const int&const int&
const int&& value;intintconst intconst int&const int&const int&
f ( 1 )   // 何か右辺値intinterrorerrorintint&&
  • Tの時、constも参照も取れるのは、値渡しでできた複製(仮引数)がどうなろうとコピー元(実引数)には関係ないからです。
  • エラーが出るのは、右辺値を書き換えても意味がなく、リテラルの参照が存在したらわけわかめになるからだとおもいます。(適当)
  • ユニバーサル参照はいろいろ変態なので覚えるのが大変そうです…

 

 

・template? 知らない子ですね

  君は、関数にを渡すだけの退屈な日々を過ごす中で、
  型も渡せたらいいのにと思ったことはあるかい?
  そういうことだよ。()

 

 

・深淵を覗きに行くために…
 (闇のコードを読むことに興味がある方は知っているべき?)

ついでにポインタ、配列、関数も入れてみましょう

T + & + αTT&T&&
TargTargTarg
const int * const value;const int *const int *const int * constconst int * const &const int * constconst int * const &
int value[4];int *int *int [4]int (&)[4]int [4]int (&)[4]
void value( int );void (*)(int)void (*)(int)void (int)void (&)(int)void (int)void (&)(int)
  • int value[4] は長さ4の int 型配列、void value( int ) は int 型引数一つを受け取る void 関数です。
  • 注目するは青文字です。値渡しでは配列や関数をポインタとして推論しています。これはC言語由来の動作で、配列や関数がポインタに成り下がる(本当はポインタじゃないけどポインタのふりをする)という動作をするためです。
  • 参照渡しはポインタのふりをする必要がないため、配列・ポインタの参照型として推論されます。

 

Tにconstつけるとこうなります。
 (VS2017でこうなるを確認できたけど理由がよくわかってなかったり…

T + const + &const Tconst T&const T&&
TargTargTarg
int value;intconst intintconst int&errorerror
int& value;intconst intintconst int&errorerror
int&& value;intconst intintconst int&errorerror
const int value;intconst intintconst int&errorerror
const int& value;intconst intintconst int&errorerror
const int&& value;intconst intintconst int&errorerror
f ( 1 )   // 何か右辺値intconst intintconst int&intconst int &&
  • const T&& は要らない子だと思ってたけど右辺値だけ受け取りたいときに使える…?
  • 右辺値と右辺値参照が頭の中でこんがらがってあぁぁぁぁ 
  • 迷ったら const 参照使ってれば一番よさそうですね

 

ポインタになるとこうなります
  ※ const が長くて表にはまらなかったのでcとしました

T + const + *T *T * constconst T *const T * const
TargTargTargTarg
int * value;   int   int*   int   int* c int c int* int c int* c
int * const value;   int   int*   int   int* c int c int* int c int* c
const int * value;c intint*c intc int* c int c int* int c int* c
const int * cosnt  value;c intc int*c intc int* c int c int* int c int* c
  • const とポインタの関係が完璧に分かっているconst教信者の方なら、const T * とか T * const とかしたときはどうなるか、すぐに予想できましたよね?(震え声)

 

ね? templateって、素直ないい子…だよ…ね?

 

 

 

 

 

 

 

項目2 autoの型推論を理解する

 

結論templateと同じ!以上!解散!


とは言えない
みたいです…
1か所だけ異なって、auto value = {1, 2, 3, 4, 5, 12}; と{}で書いたときのみ、
value は std::initializer_list<T>という型に推論されます。

左右は全く同じ意味
auto i = 1;int i = 1;
const auto ci = i;const int ci = i;
const auto d = 1.0;const double d = 1.0;
auto d2 = d;double d2 = d;
auto& ir = i;int& ir = i;
const auto& cir = irconst int& cir = ir;
auto& cir2 = cir;const int& cir2 = cir;
auto hentai = {1};std::initializer_list<int> hentai = {1};
auto hentai = {1, 1.0};error (intなのかdoubleなのか、少しでも曖昧なため)

 

 

・auto? 知らない子ですね 

  C++にはイテレータというtemplateクラス版ポインタがあるんだけど、
  そいつが指す変数の「」を取得するためにこんなコードは書きたくないよね?

  template<typename Iterator>
  void PrintValue(Iterator iterator)
  {
      std::iterator_traits<iterator>::value_type value = *iterator;
      std::cout << value;
  }

  大丈夫。こんなの覚えなくていい。そう、autoならね。

  template<typename Iterator>
  void PrintValue(Iterator iterator)
  {
      auto value = *iterator;
      std::cout << value;
  }

 

 

・深淵を覗きに行くために…

ラムダ式の引数や関数の戻り値など、関数の定義等にもautoを使えますが、
これはtemplateの型推論になるらしいです。

    auto lambda = [](auto arg){ };
    lambda({1, 3, 4});   // error std::initializer_list<int>と推論できない

    auto plsGiveMe_initializer_list()
    {
        return {1, 2, 3};    // error std::initializer_list<int>と推論できない
    }

また、std::initialzier_list<>の型推論には以下のようにC++のバージョンに左右される面があります。(デフォルトのVisualStudio2017は、C++17以降の方の動作をします。)

ソースコード推論結果
C++14以前C++17以降
auto value = {0, 1, 2};std::initializer_list<int> value = {0, 1, 2};std::initializer_list<int> value= {0, 1, 2};
auto value = {0};std::initializer_list<int> value = {0};std::initializer_list<int> value = {0};
auto value{0, 1, 2};std::initializer_list<int> value = {0, 1, 2};error
auto value{0};std::initializer_list<int> value = {0};int value = 0;

 

 

 

 

 

 

 

 

項目3 decltypeを理解する

 

結論 : そのまんま型が返ってくる!(大体そのまんま)

 

decltypeにも例外的な動作が一つあります。
単純に名前ではない左辺値式をあたえたときは
参照がもれなく付いてくるというものです。

int n;                         // 左辺値式 n
decltype(n) → int      // 単純に名前だけの左辺値式
decltype((n)) → int& // この場合は()が付いたため複雑な左辺値式と判定されるらしい…

 

項目2の変数と次の関数を使います。   void kansu(int, double){}  

左右は全く同じ意味
decltype(i)int
decltype(cir2)const int&
decltype(hentai)std::initializer_list<int>
decltype(kansu(0, 0))void
decltype(kansu)void ( int, double )
decltype((i))int&      // 唯一の例外
decltype((i + 1 + 0.1))double  // 右辺値式は例外に含まれない

 

 

・decltype? 知らない子ですね

知らなくても大丈夫。ゴリゴリtemplate書くようになるまで必要になることはないから。
それに、必要になったらいやでも覚えなきゃいけないから。(未来の自分がそう言ってる気がする)

 

・深淵を覗きに行くために…

decltypeは、autoの用に変数の型・関数の戻り値を推論するのに使えます。書式は以下の通りです。
ただし推論の方法はdecltypeのものであり、auto や template とは異なります。

左右は全く同じ意味
decltype(auto) value = 0;
decltype(auto) func()
{
    return 0;
}
int value = 0;          // 0 はint型
int func()                 // 0 はint型
{
    return 0;   
}
const int n = 0;
decltype(auto) value = n;
decltype(auto) func()
{
    return n;
}
cont int n = 0;
const int value = n;  //templateやautoはintと推論
const int func()        //templateやautoはintと推論
{
    return n;
}
int n = 0;
decltype(auto)
value = (n); // 複雑な左辺値式
decltype(auto) func()
{
    return (n);     // 複雑な左辺値式
}
int n = 0;
int&
value = n;          // 参照が付きます
int& ILoveDecltype() // 参照が付きます
{
    return n;
}
decltype(auto) value = {1, 2, 3};
decltype(auto) func()
{
    return {1, 2, 3}; // std::initializer_list<int> ?
}
error
(autoを使いますが、型の推論方法はdecltypeのものです。そのためstd::initializer_list<int>とは推論できません。)

 

 

 

 

 

 

 

項目4 推論された型を確認する

 

結論 : IDEに頼ろう!

IDE(VisualStudioとかとかとか)で気になるトークンにマウスオーバー。
これが一番(個人比)
Boost.TypeIndexというBoostライブラリも紹介されていました。
でも変な型が出る可能性は十分あるという…

そのほかにはコンパイルエラーで確認する方法、場合によっては見づらい・不正確な型名を表示する可能性がある方法なども紹介されていました。

 

 

 

 

 

 

 

< 前の項目へ   次の項目へ >

次期ゲームジェネレーター(仮) について少し(部員向け)

サバです。

最近は次期ゲームジェネレーター向けにしこしこコーディングしてました。

ゲームジェネレーターというのは学祭の際にゲームを一覧表示にして起動までしてくれるあの便利ソフトのことです。今の院生の先輩、通路さんが2年前に作成してくださいました。

これの改良版を作ろうということで、今のところはこしょう氏がUIの作成、私がファイル管理の作成を行っています。進捗はよくわかりません!(がばがば計画性)

そんでやってたらなんかよくわからなくなったので、TNPのグーグルドライブにぶん投げといたんで暇な人は見……たら頭を痛くするし、暇でない人も頭を痛くするのでなるべくご覧にならない方がいいかもしれないです。(パスは 共有フォルダ>資料 内です)

ソースにコメントはないものだと思ってください。メイン関数にSiv3DのGUIを使った超簡易版のジェネレータ()を試しに作ってあります。

ああ、言い忘れました。環境は VS2017 Community C++ と Siv3D August 2016 v2 です。

Siv3DはVS2017を推奨していませんが今のところ問題がないのでこの組み合わせです。