投稿

11月, 2019の投稿を表示しています

C++11の機能 (関数のdefault・delete宣言, overrideとfinal指定子, 移譲コンストラクタ, 継承コンストラクタ)

C++11の機能を紹介するシリーズ第四弾です。 今回は関数のdefault・delete宣言、overrideとfinal指定子、移譲コンストラクタと継承コンストラクタを紹介していきます。 関数のdefault・delete宣言 コンストラクタや代入演算子などにはユーザーが指定しなくても、必要に応じてコンパイラがそれらを自動的に作ってくれます。 ですが、「デフォルトで作ってほしくないんだよ」とか「コピーはしてほしくない」とか「この関数は呼べないようにしたい」という需要に答えるとなると、privateな部分に宣言だけ書いておくなど、あんまり綺麗でないやり方をするしかありませんでした。 そういうときに使うことができるのがdelete宣言です。delete宣言された関数は暗黙的に実体化されること無く、delete宣言された関数を必要とするコードが存在した場合コンパイルエラーになります。 対して、default宣言は、コンパイラが自動的に生成してくれるコードを明示的に生成させる宣言です。コピーやムーブで特殊な処理を行わない場合などはいちいち書くのは面倒で間違いの元になります。こういったときに使えます。また、virtualやinlineなどの指定子をつけた上でdefault宣言することもできるので、「仮想関数にするけど処理自体はコンパイラのデフォルトの動作で構わない」みたいな場合にも使えます。 struct Klass { Klass() = default; // デフォルトのコンストラクタはコンパイラのデフォルトで生成 Klass(const Klass&) = delete; // コピーコンストラクタは削除 Klass(Klass&&) = default; virtual ~Klass() = default; // 仮想関数としてデストラクタをデフォルト生成 }; overrideとfinal指定子 overrideはあのオーバーライドです。この指定子をつけることでこの関数はオーバーライドしているということを明示することができます。override指定子を付けているにもかからわずオーバーライドできていない場合はコンパイルエラーになります。なので、間違ってオーバー

C++11の機能 (constexpr, 右辺値参照・ムーブセマンティクス)

C++11の機能を紹介するシリーズ第三弾です。 今回は、constexprと右辺値参照・ムーブセマンティクスを紹介します。 constexpr constexprはコンパイル時定数を表現するための機能です。 コンパイル時定数はコンパイル時には値が確定している値のことで、コンパイル時定数になっていると展開されたり先に計算が行われるなどがされ実行時の効率が向上する可能性があります。 constexprは変数、関数、コンストラクタにつけることができます。 constexpr変数 変数につける場合はconstの代わりに記述することができます。 constexpr int ZERO = 0; このように記述するとZEROがコンパイル時定数になります。 コンパイル時定数はテンプレート引数として渡せます。 template<int N> void func() { ... } // 使う constexpr int ZERO = 0; func<ZERO>(); constexpr関数 関数にconstexprをつけると定数式が必要な場合にコンパイル時に計算されます。 逆に言うと必要のない場合や引数がコンパイル時定数でない場合は実行時に普通の関数として使われます。 C++11のconstexpr関数はreturnはひとつだけifやwhileなどの制御分が使えないなど、かなり制限がきついので、C++11のレベルで使うのは余りおすすめできません。C++14以降になるとこの制限がだいぶ緩くなるのでかなり使いやすくなりました。 constexpr int square(int n) { return n * n; } 右辺値参照とムーブセマンティクス 右辺値参照 右辺値参照はその名の通り右辺値を参照する参照です。 右辺値は簡単に言うと名前のないオブジェクトや破棄しても問題ないオブジェクトのことです。 この機能が登場するまでは右辺値も左辺値も同じ参照を使って参照していました。ですが、破棄しても問題ないオブジェクトと、破棄していけないオブジェクトが区別できないと、無駄な処理をしてしまうなどの問題があります。 これを区別するために登場した機能が右辺値参照です。

Express.jsライクな簡単なWebアプリケーションフレームワークを作った話

リポジトリは ここ(GitHub) です。ライセンスはApache License 2.0です。 これを使って何か起きても責任は負いません。 Express.jsはNode.jsで動作するWebアプリケーションフレームワークで、MEANスタックのEです。JavaScriptで簡単に書けて便利なフレームワークです。 これっぽいものをC++で実装したのがこれです。 使い方はできるだけ本家に近づけましたが、足りない機能がかなりたくさんあります。(1ヘッダだから許して...) とりあえず、C++でRESTful APIを作りたいという人(いるのかわからないですが)におすすめです! 使い方 test.cppを見ていただければ分かると思いますが、とりあえず書いておきます。 express::Expressのインスタンスを作成します。 get, post, put, del関数を使ってハンドラを登録します。 listen関数を呼んで実行を開始します。 パスの書き方は/abcみたいにパスにパラメータがないものと/abc/:idみたいにパスにパラメータがあるものが書けます。 :idの部分がパスパラメータになります。パスパラメータの値はreq.params("id")のような形で取得できます。 #include "express.hpp" #include <iostream> int main() { express::Express app; app.get("/", [](const express::Request &req, express::Response &res) { std::cout << "ok /" << std::endl; res.end("ok"); }); app.get("/parameters/:id/others/:other", [](const express::Request &req, express::Response &

C++11の機能 (initializer lists, ラムダ式, nullptr, noexcept)

C++11の機能を紹介するシリーズ第二弾です。 今回はinitializer listsとラムダ式、nullptr, noexceptを紹介します。 initializer lists initializer listsは初期化子リストという日本名がありますが、ユーザー定義型のオブジェクトを配列みたいに初期化できるようにする構文です。 第一弾に説明はありませんがサラッと出てきました。 std::vector<std::string> container = {0, 1, 2, 3, 4}; この{0, 1, 2, 3, 4}の部分がinitializer listです。 このinitializer listで初期化することをlist initialization(リスト初期化)と呼びます。 このinitializer listsを使う場合はstd::initializer_list<T>型を受ける必要があります。 struct Klass { Klass(std::initializer_list<int>) {} }; // 使う例 Klass k1 {0, 1, 2}; Klass k2 = {0, 1, 2}; ラムダ式 ラムダ式は無名関数とも呼ばれる構文です。 JavaScriptやC#など最近の言語には大体あります。 C++のラムダ式は関数オブジェクト(関数みたいに呼べるオブジェクト)を簡単に作ることができる機能です。この機能で関数を引数に取ったり返り値の値として使ったりという、いわゆる高階関数が非常に使いやすいものになります。 この機能によって関数を第一級オブジェクトみたいに扱えるようになります。 では、まずは基本的な文法から解説していきます。ラムダ式の厳密な文法は後述するリファレンスを参照していただけると幸いですが、さらっと使う分には大丈夫な程度には説明します。 auto square = [](int n) { return n * n; }; // 使う square(2); // 4 この例では引数に取った値を二乗するラムダ式を作成しsquareという変数に代入しました。 ラムダ式は[](int n) {...}の部分がラ

C++11の機能 (型推論, 範囲for)

C++11は2011年に策定された規格でだいぶ経つ仕様ですが、今でも頑張ってこのバージョンしか無理みたいな環境があります。 組み込みとかCentOSとか そういう環境を使っている人向けに便利だなーと思う機能をいくつか紹介していきたいと思います。 型推論 型推論は一般に右辺の値から左辺の新しい変数の決定したり、逆に左辺の型から右辺の型の一部を補完したりという、要するに型をいちいち書かなくても良くする機能です。 前者の型推論を持つ言語としてはC#, Dart, Kotlin, Java(10以上)などがあります。 後者の型推論を持つ言語としてはJavaがあり、ダイアモンド演算子と呼ばれています。こんな機能にどんな意味があるんだとか言われていましたがないよりはあったほうが圧倒的に便利です。 C++は前者のパターンの型推論です。 C++の型推論は次のように書きます auto number = 1; この機能はC++としては珍しく破壊的変更が行われた機能です。 どういうことかというとautoは自動変数(ローカル変数)という意味の記憶域クラス指定子でした。 ですが、これってあんまり使われて無くねということになりautoは自動なので自動的に型が決定することになぞらえたのか型推論を表す予約語になりました。 上記の例はintに推論されます。 intだとあんまり便利さを感じません(autoのほうが長いですし)が次のような型ではめちゃくちゃ便利だと思います。 std::unordered_map<std::string, std::vector<std::string>>::const_iterator itr = ...; auto itr = ...; この例はunordered_mapという型のイテレータですが随分長ったらしい型がautoの4文字になっています。 こんな感じでコード量を小さくできるのが魅力です。 範囲for (Range-based for loop) 範囲forはいわゆるforeachに当たるループです。 C++20からRangesライブラリが入りますがそれの先触れのような機能で、イテレータで範囲が表せるコンテナの走査ができます。 std::vector<std::strin

初めの挨拶

このブログではプログラミング関係の解説や備忘録、こんなの作ったよみたいなことを上げていこうかと考えています。 以後よろしくお願いします。