11月末報告会

皆さんどうもこんにちは!Web担当のでったーでございます。本日は11月の月末報告会として部員同士でゲーム製作やその他担当箇所の進捗を確かめ合いました。(皆さん忙しかったみたくて進捗は厳しい状況・・・)

ここからはゲームとしてある程度形になっている作品をご紹介していきます!


製作者:まんじゅう(初年次)

製作物:RPG


製作者:でんそん(初年次)

製作物:2Dゲーム


製作者:たか(初年次)

製作物:2Dシューティング


製作者:いっちー(2年次)

製作物:RPG


製作者:Yuta(2年次)

製作物:2Dシューティング

 


製作者:GoldenRiver(3年次)

製作物:ホッケーゲーム


この他にも様々なゲーム案が報告されコミケに向けていい作品を提供するべく現在頑張っておりますので応援よろしくおねがいします!

そろそろサイトを弄っていこうかという話

どうも皆さん、お初にお目にかかります。Web&Twitter担当となりました初年次のでったーと申します。

 

「もうそんな季節か」と思う人もいれば「この時期に「初めて」?」と思う人もいるでしょう。TNPにとって11月はそろそろ初年次が表に出てくる時期なんです(恐らく)。自己紹介とかもしたいところですけどそれはまた今度にするとします。

 

11月、TNPは秋大祭を終えて代替わりの季節、今まで役職がなかった初年次にも様々な役職が割り振られるわけであります。新しいオリジナルゲーム等の開発にも本腰を入れ一気にサークルに新しい雰囲気が流れ込んでくるんじゃないのかな、と初年次ながら期待に胸膨らませている次第なんです。

 

新しい役職云々に関しては恐らく二年次が後々記事にしそうなところなんで省略するとして今回の題材は僕の担当する「TNP公式サイト」についてです。

 

WordPressをプラットフォームとしたこの公式サイトは昨年の今頃、現三年次のガナリヤさんによって作られたものでサークル全体としては3つめのサイトだったりするらしいです。(統合はかなり大変とかなんとか)

 

まあ非常に大雑把に事を話すと今のサイトの改造権限は僕にあるわけです。引き継がれたんだから当然のことです。

 

だから改造したい!!しよう!!と意気揚々とサイトの工事に入ろうとしたのですが・・・

 

知 識 不 足

 

というわけでありまして・・・まったくもって現状目に見えて弄った点はたったの一つという有様・・・全て俺が悪いよ、申し訳ない。お恥ずかしい限りですが取り敢えず唯一の改造点を今回ご報告しておきます。

 

 

改造点

サイドバーにTNP公式アカウント(TNP子)のツイートを表示するようにしました。

 

と大層なアップデートのように見せてはいますがからくりはTwitter Publishを用いて得たHTMLをサイドバーのカスタムHTMLウィジェットに張り付けただけです・・・ただ「それっぽさ」を追及してるだけですから!!

 

とまあこんなもんです。しかしこれでもトップページにサイドバーが表示されないんで「(トップページにだけ)目立たない改修」というか・・・もう少し仕様を調べてちゃんと表示されるようにしたいですね。

 

 

これからの目標

もっとなんかいい感じにしたい(雑)。分かりやすく軽快にをモットーに改造していきたいなぁというのが今のところの考えです。もっとも自分のスキルアップが第一なんですけどね。

 

あとブログも連載的な企画とかもできるならやってみたいなぁと、これは部員のみんなの協力が不可欠ではあるんですがね・・・(それ以前にお前は何様だという問題が生じますが)

 

取り敢えずこれからのTNPブログにどうぞゆるくご期待ください!でったーでした!

TNGとは?

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

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

 

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

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

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

 

AtCoder Beginner Contest 112

こんばんえるえる〜〜〜〜
ガナリヤです
大学の後期がついに始まりましたね
今週一週間は毎日しんどくて未だに体の疲れが取れません。
コミュニケーション能力を付けないとこのままではまずい気がします。

今回は、AtCoderBeginnerContest112に出ました。
結果としては、ABCD4完は出来ましたが、Cで4WAしているのでもうちょっと細かい部分に意識をしないといけませんね・・・
早速内容を振り返っていきましょう!


A 「Programming Education」

問題文

AtCoderはクソデカ年商となり、プログラミング教育をするようになった。
入力から自分の年齢Nが入力される。
N=1なら”Hello, World”と出力せよ。
N=2ならA+Bの答を計算せよ。

解法

やるだけです。
もう20秒ぐらい早くとけるようになりたいです。


B 「Time Limit Exceeded」

問題文

Xさんは、外から家に帰ろうとしています。
現在位置から家に帰るルートは、N個存在しますが、それぞれに係る時間tiと、かかるコストciがあります。
時間T以内に、帰らなければいけない時、最小のコストを求めてください。

解法

時間T以内なので、時間T以内のルートのみを列挙して、さらにその中でも一番コストの低いものを出力してしまえばいいです。
気をつける点としては、ルートが一つもない場合はTLEと出力しないといけないので、最初の変数の初期化で上限いっぱいにしておいて、値が更新されてなければTLEとしないといけません。
実装に2分もかかったので半分ほどにするひつようがあります。

コード


int main() {
 
    LL N, T;
    cin >> N >> T;
 
    LL cost = LONG_LONG_MAX;
    for (int i = 0; i < N; i++) {
        LL c, t;
        cin >> c >> t;
        if (t <= T) {
            S_MIN(cost ,c);
        }
    }
 
    if(cost == LONG_LONG_MAX){
        OUT_L("TLE");
    }else{
        OUT_L(cost);
    }
 
    return 0;
}


Pyramid

問題文

問題文長すぎるのでここを見て→ https://beta.atcoder.jp/contests/abc112/tasks/abc112_c

解法

内容としては簡単だったのですが、3つ特殊ケースがあり、そのケースに気づくまで時間がかかってしまいました。

制約を見るとCxもCyも0~100以内なので、全探索しても余裕で間に合うことが分かります。
中心のCx、Cyを仮に全探索してみます。
すると、中心の高さHを求めたいわけですが、この高さは他の点1つを選んでしまえば逆算することが出来ます。
そうして求めたHと他の点を全て比べて、条件を満たせばそれが答えになります。

しかし、この解法だけだとうまくいかない場合があり、参考にする点が高さ0の場合です。
高さ0の場合、もともと高度は(H -|X – Cx| – |Y – Cy|, 0)のため、マンハッタン距離で0だったのか、最初から0だったのか判断することが出来ません。
よって、関係ない答を出力してしまう可能性があります。
よって、そのケースは省いて計算する必要があります。
体感的にBレベルでしたが、特殊ケースだけはDレベルな感じがしました。

コード

int main() {
 
    int N;
    cin >> N;
 
    VLL x(N), y(N), h(N);
    REP(i, N) cin >> x[i] >> y[i] >> h[i];
 
    for (LL i = 0; i <= 100; i++) {
        for (LL j = 0; j <= 100; j++) {
            LL H = 0;
            for (LL k = 0; k < N; k++) {
                if (h[k] != 0) {
                    H = h[k] + ABS(i - y[k]) + ABS(j - x[k]);
                    break;
                }
            }
            if (H <= 0) continue;
            bool good = true;
            for (int k = 0; k < N; k++) {
                LL _h = C_MAX(H - ABS(i - y[k]) - ABS(j - x[k]), 0LL);
                if (_h != h[k]) good = false;
            }
            if (good) {
                cout << j << " " << i << " " << H << endl;
                return 0;
            }
        }
    }
 
    return 0;
}


D Partition

問題文

整数N, Mが与えられます。
a1 + a2 + ・・・ + aN = Mとなるような長さNの数列aにおいて、a1~aNの最大公約数となる最大値を求めてください。

解法

Mが与えられるので、可能性のあるaの組み合わせを作成し、その最大公約数を求める必要があります。
最大の最大公約数であるため、少し工夫しないといけません。なぜなら、制約でNが10万のため、計算量的にNlogNが最大であるためです。

そこで、最大公約数について一度考え直して見ます。
最大公約数は、a全てにおいて、共通の約数を持っており、共通の約数ということは同じ倍数を持っているということに等しいです。
共通の倍数をKとすると
Kb1 + Kb2 + ・・・ + KbN = Mということになります。
つまりM = K * (b1 + b2 + ・・・ + bN)というわけで、Mは因数としてKを持つことが分かります。
よって、Mに対して、約数を列挙し、その約数の中で、NをかけてもMを超えないものが答となります。
約数をTとして、T
N < Mのとき、数が余らないか?というう話になりますが、Mも因数Kを持つため、うまく配分すれば全ての変数がKを因数に持つことが分かります。

素因数分解はO(√N)でできるので、計算量的にも間に合うことが出来ます。

今回のDは3分で通せたので、成長を感じます。(なお、C)

コード



//nの約数
template<typename T>
vector<T> DIVISOR(T n) {
    vector<T> v;
    for (LL i = 1; i * i <= n; ++i) {
        if (n % i == 0) {
            v.push_back(i);
            if (i != n / i) {
                v.push_back(n / i);
            }
        }
    }
    sort(v.begin(), v.end());
    return v;
}
 

int main() {
 
    LL N, M;
    cin >> N >> M;
 
    auto divisors = DIVISOR(M);
    LL maxV = 1;
 
    for (int i = 0; i < SZ(divisors); i++) {
        if(divisors[i] * N <= M){
            maxV = divisors[i];
        }
    }
    OUT_L(maxV);
 
    return 0;
}
 
 

### 感想

今回の問題は、Cが一番難しかった感じがあります。
4WAは流石に出しすぎなので、もうちょっとミスを減らさないといけませんね・・・

ガナリヤでした!

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

 

 

 

 

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

コミケ2018夏 お品書き

特設サイトはこちら←


2018コミケ夏!来る!

こんばんえるえる〜〜〜〜。
TNPのWeb担当ガナリヤです!(ガナリヤはWeb言語が言うて得意ではない。誰か変わって・・・)

今日は2018夏コミックマーケットの、TNPの出品作品を見ていこうと思います!
特設サイトは上記のリンクにありますので是非!

 

表紙

まずはこちらの表紙ですね!
二年次の蟹さんの作品です!

かわいい!!!!!
これまでのTNP子ちゃんよりも、デジタルでVTuberっぽい作風となっています。
これはもうジャケ買いする人がでてくることでしょう・・・(中身でも勝負しろ(迫真))

今回のTNPBOXのTNP子ちゃんがVTuberっぽい作風になったのは訳があります。現在二年次のうおちーさんが、TNP念願のTNP子VTuber化を図って躍進してくれています。TNP子のVTuberデビューも近い・・・?
そして、そのVTuberを意識して蟹さんにイラストを書いていただきました!
これまでのTNP子になかった猫耳属性や、スカート姿など、新たな新生TNP子が垣間見えた気がします!

表紙が可愛いのは認める。ただ、表紙だけほしいっていう声はゆるさない!

 

UniPyon

ゲーム紹介に入っていこうと思います。
まずはこちらの作品「UniPyon」。製作者は我らが部長こしょうです。

こちらのゲームでは、Unityちゃんを操作してウニティを中央のゴールにゴールするといったゲームです!
僕と同じくコミケ終盤で一気に詰め込んだゲームながら、クオリティは素晴らしく、とくにグラフィック面はもうただただすごいことになってます(動画でみせたい)!
音楽に合わせてウニティや波形が動いたり、発光を上手に使っているなど、とても楽しい雰囲気になっています!

ただ、僕が一番好きなのはこの部分。

タイトル画面がわかりやすい!そして、タイトルで遊べる!!!これ大事な要素です!
タイトルで遊べるゲームは神ゲー。はっきりわかんだね。

非常に素晴らしい作品になっています。
是非ご試遊ください!

  

JumpingBox

次はこちらの作品「JumpingBox」。
製作者は、二年次うおちーです。(二年次のほうが三年次より作品を多く作っている・・・うっ頭が・・・)

ゲームライブラリはSiv3D!
うおちーもSiv3Dの便利さに気づいてしまったようです。
ゲーム内容としては、自動生成される足場を踏んでいきながら、落ちないようにできる限り上に登り続けていくというゲームです。
こういうシンプルなゲームはつい、やり続けてしまうという魔性の力があります・・・(3敗)
来年は、老害ながら、二年次がものすごく優秀なので何も心配がないです。(老害ムーブ(三年次もがんばるぞ))

   

UFOキャッチャー

続いてはこちらの作品「UFOキャッチャー」。
製作者は、二年次yutaくんです(強すぎぃ!)。

こちらのゲームはUnityでできており、3Dゲームでの参戦です。
ゲーム内容は、アームでグッズをふんっ!ってやる、そうUFOキャッチャーそのものです!
Unityでこういった物理演算をやるのは非常に難しく、スクリプトもだいぶ増えてしまうため大変なのですが、素晴らしい出来になっています。
モードも複数用意されており、練習モードで鍛え上げてから本戦モードに望むことができます。

そして、一番僕が好きだったのが、発表の際の物理演算バグです。
やっぱり、3Dゲームは時たまにバグってしまうのが最高なんだなと勉強になりました。
僕も制作活動頑張ろうと思います。(2年次にも1年次にも負けられない!)

 

Just Do It!

続いてはこちらのアプリ?「JustDoIt」
製作者は、ガナリヤ(僕)です。

JustDoItにはまり、パソコンのスリープを解除したときに自動でJustDoItおじさんの声が流れると勉強頑張れるんじゃないか?と思って作りました。

ライブラリはElectronを使用しており、解説はhttps://qiita.com/ganariya/items/6c9ca69ca1c89282c228に書いてます。こちらのページからダウンロードもできます。

TNPはゲームだけと思われがちですが、イラスト・音楽、そしてアプリ制作もあるんだぞ!
というところも見せていきたいです。


        
    

以上が今回の作品となり・・・

・・・あの作品を忘れているじゃないか!
   

TNG

自分がMacでTNGを起動できなかったため画像はお見せできないのが悲しいのですが・・・
こちらのTNGは、三年次のサバとこしょうの共同制作になっています。

こちらの作品TNGは「TNP Generator」 の略であり、ゲームでなく、アプリとなっています。
使用ライブラリはUnityであり、TNPの作品をデータベースに登録し、検索をすると自動で起動してくれるという代物です!
ソースコードは五万行を超えているとかなんとか・・・(すごい(絶句))
今回の配布はTNGも入っているらしく、サバとこしょうの一年間の集大成が詰まっています。特に、サバはほぼ毎日こちらの作品に力を注いでおり、一時期はSiv3Dで実装していましたが、ライブラリの関係上、一度すべてUnityに移し、更に開発を進めていました!(すごい(悶絶))
 
今回のTNP_BOXの中で、一番やばい作品だと思います!
是非!明日のコミックマーケットで触っていただければと思います!やばいです!!!!!!!!

 

まとめ

以上が、今回の夏コミのTNPBOXのお品書きになります!
非常に濃厚な中身になっており、特に二年次の躍進、そして、サバ・こしょうの血の結晶が垣間見えたTNPBOXになっていると思います。

是非明日はTNPBOXを手にとっていただければと思います。

明日 8月10日西地区よ-24aにて、TNPBOXとともにお待ちしています!!!!!!

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++に追加されたということは、いいからだまって使えという機能のはずですから。(盲信)

 

 

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