Update cpp authored by umaumax's avatar umaumax
[[_TOC_]] [[_TOC_]]
## コーディング時の注意事項 ## コーディング時の注意事項
* [CERT C コーディングスタンダード 00\. はじめに]( https://www.jpcert.or.jp/sc-rules/00.introduction.html ) * [CERT C コーディングスタンダード 00\. はじめに]( https://www.jpcert.or.jp/sc-rules/00.introduction.html )
* [Google C\+\+ Style Guide]( https://google.github.io/styleguide/cppguide.html ) * [Google C\+\+ Style Guide]( https://google.github.io/styleguide/cppguide.html )
### 関数の出力 ### 関数の出力
[Google C\+\+ Style Guide -Inputs_and_Outputs]( https://google.github.io/styleguide/cppguide.html#Inputs_and_Outputs ) [Google C\+\+ Style Guide -Inputs_and_Outputs]( https://google.github.io/styleguide/cppguide.html#Inputs_and_Outputs )
* 基本的に戻り値を利用すること * 基本的に戻り値を利用すること
* どうしても引数にする場合 * どうしても引数にする場合
* 入力引数の後に出力引数の順番とする * 入力引数の後に出力引数の順番とする
* 出力/入出力の変数はnon-const pointerを利用すること * 出力/入出力の変数はnon-const pointerを利用すること
e.g. `void Foo(const string &in, string *out);` e.g. `void Foo(const string &in, string *out);`
なお,入力としてポインタの情報が欲しい場合や`nullptr`である可能性がある場合には,`const T*`を利用しても良い(これは出力に`T&`ではなくポインタを利用するメリットの1つでもある) なお,入力としてポインタの情報が欲しい場合や`nullptr`である可能性がある場合には,`const T*`を利用しても良い(これは出力に`T&`ではなくポインタを利用するメリットの1つでもある)
[C\+\+ Core Guidelines: The Rules for in, out, in\-out, consume, and forward Function Parameter \- ModernesCpp\.com]( https://www.modernescpp.com/index.php/c-core-guidelines-how-to-pass-function-parameters ) [C\+\+ Core Guidelines: The Rules for in, out, in\-out, consume, and forward Function Parameter \- ModernesCpp\.com]( https://www.modernescpp.com/index.php/c-core-guidelines-how-to-pass-function-parameters )
では,`out`なケースは単独でも複数でも返り値として返す方法を採用し,`in-out`の場合はnon-const reference(`T&`)を採用している では,`out`なケースは単独でも複数でも返り値として返す方法を採用し,`in-out`の場合はnon-const reference(`T&`)を採用している
* [void foo\(T& out\) \- How to fix output parameters]( https://foonathan.net/2016/10/output-parameter/ ) * [void foo\(T& out\) \- How to fix output parameters]( https://foonathan.net/2016/10/output-parameter/ )
* [Input\-output arguments: reference, pointers or values? · Mathieu Ropert]( https://mropert.github.io/2018/04/03/output_arguments/ ) * [Input\-output arguments: reference, pointers or values? · Mathieu Ropert]( https://mropert.github.io/2018/04/03/output_arguments/ )
個人的には 個人的には
* ポインタ利用: null pointerチェックがあるので,面倒であるし,バグの温床 * ポインタ利用: null pointerチェックがあるので,面倒であるし,バグの温床
* リファレンス利用: 呼び出し側で初期化忘れ防止の初期値が無駄になり,関数内でエラーでの早期returnのときに値を初期値にするのかという問題がでてくる * リファレンス利用: 呼び出し側で初期化忘れ防止の初期値が無駄になり,関数内でエラーでの早期returnのときに値を初期値にするのかという問題がでてくる
* 返り値で返す: 実はgoogle coding styleにも合致する方法で,良いとこどりなのではないか? * 返り値で返す: 実はgoogle coding styleにも合致する方法で,良いとこどりなのではないか?
### 注意点が多く記述されているFAQ ### 注意点が多く記述されているFAQ
[C\+\+ FAQ]( https://isocpp.org/wiki/faq ) [C\+\+ FAQ]( https://isocpp.org/wiki/faq )
### クラスのstaticフィールドの利用方法 ### クラスのstaticフィールドの利用方法
[C\+\+の class template を使えば static メンバの実体がヘッダファイルに書けるカラクリ \- higepon blog]( https://higepon.hatenablog.com/entry/20100803/1280834422 ) [C\+\+の class template を使えば static メンバの実体がヘッダファイルに書けるカラクリ \- higepon blog]( https://higepon.hatenablog.com/entry/20100803/1280834422 )
#### 定数値をhppではなく、cppファイルに記述する方法 #### 定数値をhppではなく、cppファイルに記述する方法
[explicit-define-static-data-mems \- Constructors, C\+\+ FAQ]( https://isocpp.org/wiki/faq/ctors#explicit-define-static-data-mems ) [explicit-define-static-data-mems \- Constructors, C\+\+ FAQ]( https://isocpp.org/wiki/faq/ctors#explicit-define-static-data-mems )
`std::string`はこちら `std::string`はこちら
[c\+\+ \- Static constant string \(class member\) \- Stack Overflow]( https://stackoverflow.com/questions/1563897/static-constant-string-class-member ) [c\+\+ \- Static constant string \(class member\) \- Stack Overflow]( https://stackoverflow.com/questions/1563897/static-constant-string-class-member )
#### 定数値をhpp、cppファイルに宣言を記述する方法 #### 定数値をhpp、cppファイルに宣言を記述する方法
[static-const-with-initializers \- Constructors, C\+\+ FAQ]( https://isocpp.org/wiki/faq/ctors#static-const-with-initializers ) [static-const-with-initializers \- Constructors, C\+\+ FAQ]( https://isocpp.org/wiki/faq/ctors#static-const-with-initializers )
### `__`(ダブルアンダースコア)は変数のどこにあっても違反 ### `__`(ダブルアンダースコア)は変数のどこにあっても違反
[syntax \- Why do people use \_\_ \(double underscore\) so much in C\+\+ \- Stack Overflow]( https://stackoverflow.com/questions/224397/why-do-people-use-double-underscore-so-much-in-c ) [syntax \- Why do people use \_\_ \(double underscore\) so much in C\+\+ \- Stack Overflow]( https://stackoverflow.com/questions/224397/why-do-people-use-double-underscore-so-much-in-c )
## C++17 ## C++17
* [variant - cpprefjp C++日本語リファレンス]( https://cpprefjp.github.io/reference/variant/variant.html ) * [variant - cpprefjp C++日本語リファレンス]( https://cpprefjp.github.io/reference/variant/variant.html )
* [optional - cpprefjp C++日本語リファレンス]( https://cpprefjp.github.io/reference/optional/optional.html ) * [optional - cpprefjp C++日本語リファレンス]( https://cpprefjp.github.io/reference/optional/optional.html )
上記は、[sewenew/redis-plus-plus: Redis client written in C++]( https://github.com/sewenew/redis-plus-plus )にて活用されている 上記は、[sewenew/redis-plus-plus: Redis client written in C++]( https://github.com/sewenew/redis-plus-plus )にて活用されている
### 🔥directory_iterator(ファイルの走査順序は未規定) ### 🔥directory_iterator(ファイルの走査順序は未規定)
* [directory_iterator - cpprefjp C++日本語リファレンス]( https://cpprefjp.github.io/reference/filesystem/directory_iterator.html ) * [directory_iterator - cpprefjp C++日本語リファレンス]( https://cpprefjp.github.io/reference/filesystem/directory_iterator.html )
* [std::directory_iterator に注意せよ]( https://zenn.dev/enchan1207/articles/29de772131de13 ) * [std::directory_iterator に注意せよ]( https://zenn.dev/enchan1207/articles/29de772131de13 )
## 終了時のエラー回避のために ## 終了時のエラー回避のために
### 基底クラスが派生クラスのリソースへ依存している際の解放順番に注意 ### 基底クラスが派生クラスのリソースへ依存している際の解放順番に注意
継承でスレッドを利用している例 継承でスレッドを利用している例
``` cpp ``` cpp
#include <chrono> #include <chrono>
#include <iostream> #include <iostream>
#include <thread> #include <thread>
class Base { class Base {
public: public:
Base() { std::cout << "Base constructor" << std::endl; } Base() { std::cout << "Base constructor" << std::endl; }
virtual ~Base() { virtual ~Base() {
std::cout << "Base destructor" << std::endl; std::cout << "Base destructor" << std::endl;
Join(); Join();
} }
void Start() { void Start() {
thread_ = std::thread([this]() { Callback(); }); thread_ = std::thread([this]() { Callback(); });
} }
void Join() { void Join() {
if (thread_.joinable()) { if (thread_.joinable()) {
thread_.join(); thread_.join();
} }
} }
virtual void Callback() = 0; virtual void Callback() = 0;
private: private:
std::thread thread_; std::thread thread_;
}; };
class Derived : public Base { class Derived : public Base {
public: public:
Derived() { Derived() {
std::cout << "Derived constructor" << std::endl; std::cout << "Derived constructor" << std::endl;
message_ = "alive"; message_ = "alive";
} }
~Derived() override { ~Derived() override {
std::cout << "Derived destructor" << std::endl; std::cout << "Derived destructor" << std::endl;
// 継承元の明示的なJoin()が必要となる // 継承元の明示的なJoin()が必要となる
Join(); Join();
message_ = "dead"; message_ = "dead";
} }
void Callback() override { void Callback() override {
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
std::cout << "Callback:" << message_ << std::endl; std::cout << "Callback:" << message_ << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(100)); std::this_thread::sleep_for(std::chrono::milliseconds(100));
} }
} }
private: private:
std::string message_; std::string message_;
}; };
int main() { int main() {
Base* b = new Derived(); Base* b = new Derived();
b->Start(); b->Start();
std::this_thread::sleep_for(std::chrono::milliseconds(300)); std::this_thread::sleep_for(std::chrono::milliseconds(300));
delete b; delete b;
} }
``` ```
``` bash ``` bash
$ ./a.out $ ./a.out
Base constructor Base constructor
Derived constructor Derived constructor
Callback:alive Callback:alive
Callback:alive Callback:alive
Callback:alive Callback:alive
Derived destructor Derived destructor
Callback:alive Callback:alive
Callback:alive Callback:alive
Callback:alive Callback:alive
Callback:alive Callback:alive
Callback:alive Callback:alive
Callback:alive Callback:alive
Callback:alive Callback:alive
Base destructor Base destructor
``` ```
### フィールドメンバがスレッド経由でクラスのリソースへ依存している際の解放順番に注意 ### フィールドメンバがスレッド経由でクラスのリソースへ依存している際の解放順番に注意
``` cpp ``` cpp
#include <chrono> #include <chrono>
#include <functional> #include <functional>
#include <iostream> #include <iostream>
#include <thread> #include <thread>
class Worker { class Worker {
public: public:
Worker() { std::cout << "Base constructor" << std::endl; } Worker() { std::cout << "Base constructor" << std::endl; }
virtual ~Worker() { virtual ~Worker() {
std::cout << "Worker destructor" << std::endl; std::cout << "Worker destructor" << std::endl;
Join(); Join();
} }
void Start(std::function<void(void)> callback) { void Start(std::function<void(void)> callback) {
thread_ = std::thread([callback]() { callback(); }); thread_ = std::thread([callback]() { callback(); });
} }
void Join() { void Join() {
if (thread_.joinable()) { if (thread_.joinable()) {
thread_.join(); thread_.join();
} }
} }
private: private:
std::thread thread_; std::thread thread_;
}; };
class Hoge { class Hoge {
public: public:
Hoge() { Hoge() {
std::cout << "Hoge constructor" << std::endl; std::cout << "Hoge constructor" << std::endl;
message_ = "alive"; message_ = "alive";
} }
~Hoge() { ~Hoge() {
std::cout << "Hoge destructor" << std::endl; std::cout << "Hoge destructor" << std::endl;
// フィールド利用の明示的なJoin()が必要となる // フィールド利用の明示的なJoin()が必要となる
worker_.Join(); worker_.Join();
message_ = "dead"; message_ = "dead";
} }
void Start() { void Start() {
worker_.Start([this]() { worker_.Start([this]() {
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
std::cout << "Callback:" << message_ << std::endl; std::cout << "Callback:" << message_ << std::endl;
std::this_thread::sleep_for(std::chrono::milliseconds(100)); std::this_thread::sleep_for(std::chrono::milliseconds(100));
} }
}); });
} }
private: private:
Worker worker_; Worker worker_;
std::string message_; std::string message_;
}; };
int main() { int main() {
Hoge hoge; Hoge hoge;
hoge.Start(); hoge.Start();
std::this_thread::sleep_for(std::chrono::milliseconds(300)); std::this_thread::sleep_for(std::chrono::milliseconds(300));
} }
``` ```
``` bash ``` bash
$ ./a.out $ ./a.out
Base constructor Base constructor
Hoge constructor Hoge constructor
Callback:alive Callback:alive
Callback:alive Callback:alive
Callback:alive Callback:alive
Hoge destructor Hoge destructor
Callback:alive Callback:alive
Callback:alive Callback:alive
Callback:alive Callback:alive
Callback:alive Callback:alive
Callback:alive Callback:alive
Callback:alive Callback:alive
Callback:alive Callback:alive
Worker destructor Worker destructor
``` ```
## 継承 ## 継承
public, protected, privateの3種類の継承があるが,通常はpublic継承を利用するので,あまり気にする場面はないと思われる public, protected, privateの3種類の継承があるが,通常はpublic継承を利用するので,あまり気にする場面はないと思われる
[非public継承の使いどころ \| 闇夜のC\+\+]( http://cpp.aquariuscode.com/inheritance-use-case ) [非public継承の使いどころ \| 闇夜のC\+\+]( http://cpp.aquariuscode.com/inheritance-use-case )
### 抽象クラスを利用するときには、ポインタとして利用する ### 抽象クラスを利用するときには、ポインタとして利用する
抽象クラスはインスタンス化できない 抽象クラスはインスタンス化できない
### コンストラクタ/デストラクタの呼び出し順序 ### コンストラクタ/デストラクタの呼び出し順序
``` cpp ``` cpp
#include <iostream> #include <iostream>
class Base { class Base {
public: public:
Base() { Base() {
std::cout << "Base constructor" << std::endl; // 1 std::cout << "Base constructor" << std::endl; // 1
} }
virtual ~Base() { virtual ~Base() {
std::cout << "Base destructor" << std::endl; // 6 std::cout << "Base destructor" << std::endl; // 6
} }
}; };
class Derived : public Base { class Derived : public Base {
public: public:
Derived() { Derived() {
std::cout << "Derived constructor" << std::endl; // 2 std::cout << "Derived constructor" << std::endl; // 2
} }
~Derived() override { ~Derived() override {
std::cout << "Derived destructor" << std::endl; // 5 std::cout << "Derived destructor" << std::endl; // 5
} }
}; };
class DoubleDerived : public Derived { class DoubleDerived : public Derived {
public: public:
DoubleDerived() { DoubleDerived() {
std::cout << "DoubleDerived constructor" << std::endl; // 3 std::cout << "DoubleDerived constructor" << std::endl; // 3
} }
~DoubleDerived() override { ~DoubleDerived() override {
std::cout << "DoubleDerived destructor" << std::endl; // 4 std::cout << "DoubleDerived destructor" << std::endl; // 4
} }
}; };
int main() { int main() {
Base* b = new DoubleDerived(); Base* b = new DoubleDerived();
delete b; delete b;
} }
``` ```
``` bash ``` bash
$ ./a.out $ ./a.out
Base constructor Base constructor
Derived constructor Derived constructor
DoubleDerived constructor DoubleDerived constructor
DoubleDerived destructor DoubleDerived destructor
Derived destructor Derived destructor
Base destructor Base destructor
``` ```
`Base``virtual`を外した場合の挙動 `Base``virtual`を外した場合の挙動
``` bash ``` bash
$ ./a.out $ ./a.out
Base constructor Base constructor
Derived constructor Derived constructor
DoubleDerived constructor DoubleDerived constructor
Base destructor Base destructor
``` ```
### `virtual`は伝播するので、継承先で明記しなくてもよい ### `virtual`は伝播するので、継承先で明記しなくてもよい
[virtual function specifier \- cppreference\.com]( https://en.cppreference.com/w/cpp/language/virtual ) [virtual function specifier \- cppreference\.com]( https://en.cppreference.com/w/cpp/language/virtual )
``` cpp ``` cpp
#include <iostream> #include <iostream>
struct Base { struct Base {
public: public:
virtual ~Base() { std::cout << "Base\n"; } virtual ~Base() { std::cout << "Base\n"; }
}; };
struct Derived : Base { struct Derived : Base {
public: public:
/* virtual */ ~Derived() override { std::cout << "Derived\n"; } /* virtual */ ~Derived() override { std::cout << "Derived\n"; }
}; };
struct DerivedParent : Derived { struct DerivedParent : Derived {
public: public:
/* virtual */ ~DerivedParent() override { std::cout << "DerivedParent\n"; } /* virtual */ ~DerivedParent() override { std::cout << "DerivedParent\n"; }
}; };
int main() { int main() {
Base* b = new DerivedParent(); Base* b = new DerivedParent();
delete b; delete b;
} }
``` ```
## enum ## enum
よく使う命名例 よく使う命名例
``` cpp ``` cpp
enum Error { kSuccess, kInvalidArgs }; enum Error { kSuccess, kInvalidArgs };
``` ```
## 構造体 ## 構造体
### 構造体/クラスのフィールドのコロンで参照bit範囲を設定できる ### 構造体/クラスのフィールドのコロンで参照bit範囲を設定できる
[c\+\+ \- ":" \(colon\) in C struct \- what does it mean? \- Stack Overflow]( https://stackoverflow.com/questions/8564532/colon-in-c-struct-what-does-it-mean ) [c\+\+ \- ":" \(colon\) in C struct \- what does it mean? \- Stack Overflow]( https://stackoverflow.com/questions/8564532/colon-in-c-struct-what-does-it-mean )
* そのフィールドにそれ以上の値を定数で代入すると、ビルド時に警告がある * そのフィールドにそれ以上の値を定数で代入すると、ビルド時に警告がある
* 超えた範囲は巡回する(参照bit範囲が固定なので、このように見えるだけでは?) * 超えた範囲は巡回する(参照bit範囲が固定なので、このように見えるだけでは?)
* 実行時にも有効で、`int v:2;`とすると、`-2,-1,0,1`の値のみを取る * 実行時にも有効で、`int v:2;`とすると、`-2,-1,0,1`の値のみを取る
## 関数 ## 関数
### `f(g(x), h(y))`の呼び出し順序は規定されていない ### `f(g(x), h(y))`の呼び出し順序は規定されていない
ちなみに、Rustは最近、left-to-rightであることが規定された ちなみに、Rustは最近、left-to-rightであることが規定された
## ラムダ関数 ## ラムダ関数
* ラムダ関数のキャプチャについて`[&]`は利用しないこと * ラムダ関数のキャプチャについて`[&]`は利用しないこと
* 基本的には、`[hoge]`としてコピーするか、引数としする方法を取り、どうしても、参照のキャプチャをしたい場合には明示的に、変数名を指定して`[&hoge]`の形式を利用すること * 基本的には、`[hoge]`としてコピーするか、引数としする方法を取り、どうしても、参照のキャプチャをしたい場合には明示的に、変数名を指定して`[&hoge]`の形式を利用すること
* `[&this]`はできないので、`[this]`となる * `[&this]`はできないので、`[this]`となる
### ローカル変数のキャプチャの遅延実行 ### ローカル変数のキャプチャの遅延実行
* ローカルな変数をキャプチャして他のスレッドに渡して遅延実行はNG * ローカルな変数をキャプチャして他のスレッドに渡して遅延実行はNG
* ダングリングポインタを参照することになり、直後に再帰関数などを呼ぶとスタックの内容が書き換わりやすい * ダングリングポインタを参照することになり、直後に再帰関数などを呼ぶとスタックの内容が書き換わりやすい
* スレッドごとに、スタックが独立しているので、スレッドによって、破壊の傾向が変化することもある * スレッドごとに、スタックが独立しているので、スレッドによって、破壊の傾向が変化することもある
* スタックの深い場所で発生すると他の箇所からの書き換わりにくいこともあり、発見しにくくなる * スタックの深い場所で発生すると他の箇所からの書き換わりにくいこともあり、発見しにくくなる
* 一見、`-O0`ビルドだとNGで`-O1,-O2,-O3`ビルドだとOKのように見えることがあるが、キャプチャ先のアドレスを参照するのでNG * 一見、`-O0`ビルドだとNGで`-O1,-O2,-O3`ビルドだとOKのように見えることがあるが、キャプチャ先のアドレスを参照するのでNG
* `-fstack-protector-all`では検出できない * `-fstack-protector-all`では検出できない
* キャプチャした後にデータを書き込んでいると、`*** stack smashing detected ***`のような結果となる可能性が高い * キャプチャした後にデータを書き込んでいると、`*** stack smashing detected ***`のような結果となる可能性が高い
* `-fsanitize=address`の場合、他でスタックが伸びて、初めてアクセスがあった場合に実行時に検出される * `-fsanitize=address`の場合、他でスタックが伸びて、初めてアクセスがあった場合に実行時に検出される
* 問題がある挙動になって初めて検出されるので、存在しないことの証明ができない * 問題がある挙動になって初めて検出されるので、存在しないことの証明ができない
### パフォーマンス ### パフォーマンス
[関数ポインタと関数オブジェクトのインライン展開 \- Qiita]( https://qiita.com/kaityo256/items/5911d50c274465e19cf6 ) [関数ポインタと関数オブジェクトのインライン展開 \- Qiita]( https://qiita.com/kaityo256/items/5911d50c274465e19cf6 )
関数オブジェクト、関数ポインタ、ラムダ関数の順に遅くなる(この場合、関数ポインタを利用しても速度が同じとなっている) 関数オブジェクト、関数ポインタ、ラムダ関数の順に遅くなる(この場合、関数ポインタを利用しても速度が同じとなっている)
``` bash ``` bash
$ g++ -std=c++11 function_object.cpp -O3 $ g++ -std=c++11 function_object.cpp -O3
$ hyperfine -m 3 ./a.out $ hyperfine -m 3 ./a.out
Benchmark #1: ./a.out Benchmark #1: ./a.out
Time (mean ± σ): 183.4 ms ± 26.3 ms [User: 170.0 ms, System: 5.2 ms] Time (mean ± σ): 183.4 ms ± 26.3 ms [User: 170.0 ms, System: 5.2 ms]
Range (min … max): 165.5 ms … 232.9 ms 12 runs Range (min … max): 165.5 ms … 232.9 ms 12 runs
$ g++ -std=c++11 funcion_pointer.cpp -O3 $ g++ -std=c++11 funcion_pointer.cpp -O3
$ hyperfine -m 3 ./a.out $ hyperfine -m 3 ./a.out
Benchmark #1: ./a.out Benchmark #1: ./a.out
Time (mean ± σ): 182.9 ms ± 32.0 ms [User: 171.3 ms, System: 5.5 ms] Time (mean ± σ): 182.9 ms ± 32.0 ms [User: 171.3 ms, System: 5.5 ms]
Range (min … max): 165.5 ms … 260.3 ms 11 runs Range (min … max): 165.5 ms … 260.3 ms 11 runs
$ g++ -std=c++11 funcional.cpp -O3 $ g++ -std=c++11 funcional.cpp -O3
$ hyperfine -m 3 ./a.out $ hyperfine -m 3 ./a.out
Benchmark #1: ./a.out Benchmark #1: ./a.out
Time (mean ± σ): 1.012 s ± 0.024 s [User: 979.7 ms, System: 8.5 ms] Time (mean ± σ): 1.012 s ± 0.024 s [User: 979.7 ms, System: 8.5 ms]
Range (min … max): 0.996 s … 1.039 s 3 runs Range (min … max): 0.996 s … 1.039 s 3 runs
``` ```
## 型 ## 型
### unsigned char型またはunsigned short型などの演算結果はsigned int型となる ### unsigned char型またはunsigned short型などの演算結果はsigned int型となる
``` cpp ``` cpp
#include <cstdint> #include <cstdint>
#include <cstdio> #include <cstdio>
#include <typeinfo> #include <typeinfo>
#define check_type(type) \ #define check_type(type) \
{ \ { \
type x = 123; \ type x = 123; \
if (typeid(x).name() != typeid(~x).name()) { \ if (typeid(x).name() != typeid(~x).name()) { \
printf("%20s type is %s\n", #type, typeid(x).name()); \ printf("%20s type is %s\n", #type, typeid(x).name()); \
printf("%20s type is %s\n", "~" #type, typeid(~x).name()); \ printf("%20s type is %s\n", "~" #type, typeid(~x).name()); \
if (typeid(~x).name() != typeid(x << 1).name()) { \ if (typeid(~x).name() != typeid(x << 1).name()) { \
printf("%20s type is %s\n", #type " << 1", typeid(x << 1).name()); \ printf("%20s type is %s\n", #type " << 1", typeid(x << 1).name()); \
} \ } \
} else { \ } else { \
printf("%20s type is %s\n", #type, "same"); \ printf("%20s type is %s\n", #type, "same"); \
} \ } \
printf("\n"); \ printf("\n"); \
} }
int main(int argc, char* argv[]) { int main(int argc, char* argv[]) {
check_type(unsigned char); check_type(unsigned char);
check_type(uint8_t); check_type(uint8_t);
check_type(signed char); check_type(signed char);
check_type(char); check_type(char);
check_type(int8_t); check_type(int8_t);
check_type(unsigned short); check_type(unsigned short);
check_type(uint16_t); check_type(uint16_t);
check_type(short); check_type(short);
check_type(int16_t); check_type(int16_t);
check_type(uint32_t); check_type(uint32_t);
check_type(int32_t); check_type(int32_t);
check_type(unsigned int); check_type(unsigned int);
check_type(int); check_type(int);
check_type(uint64_t); check_type(uint64_t);
check_type(int64_t); check_type(int64_t);
check_type(bool); check_type(bool);
// check_type(float); // check_type(float);
// check_type(double); // check_type(double);
return 0; return 0;
} }
``` ```
``` ```
unsigned char type is h unsigned char type is h
~unsigned char type is i ~unsigned char type is i
uint8_t type is h uint8_t type is h
~uint8_t type is i ~uint8_t type is i
signed char type is a signed char type is a
~signed char type is i ~signed char type is i
char type is c char type is c
~char type is i ~char type is i
int8_t type is a int8_t type is a
~int8_t type is i ~int8_t type is i
unsigned short type is t unsigned short type is t
~unsigned short type is i ~unsigned short type is i
uint16_t type is t uint16_t type is t
~uint16_t type is i ~uint16_t type is i
short type is s short type is s
~short type is i ~short type is i
int16_t type is s int16_t type is s
~int16_t type is i ~int16_t type is i
uint32_t type is same uint32_t type is same
int32_t type is same int32_t type is same
unsigned int type is same unsigned int type is same
int type is same int type is same
uint64_t type is same uint64_t type is same
int64_t type is same int64_t type is same
bool type is b bool type is b
~bool type is i ~bool type is i
``` ```
## template ## template
### インスタンス化 ### インスタンス化
例えば、テンプレートを明示的にインスタンス化して実装をcpp側にすることで、通常の関数のように扱うテクニックがある 例えば、テンプレートを明示的にインスタンス化して実装をcpp側にすることで、通常の関数のように扱うテクニックがある
* [テンプレートのインスタンス化 | Programming Place Plus C++編【言語解説】 第21章]( https://programming-place.net/ppp/contents/cpp/language/021.html ) * [テンプレートのインスタンス化 | Programming Place Plus C++編【言語解説】 第21章]( https://programming-place.net/ppp/contents/cpp/language/021.html )
* [[C++]特殊化?実体化??インスタンス化???明示的????部分的????? - 地面を見下ろす少年の足蹴にされる私]( https://onihusube.hatenablog.com/entry/2020/01/24/183247 ) * [[C++]特殊化?実体化??インスタンス化???明示的????部分的????? - 地面を見下ろす少年の足蹴にされる私]( https://onihusube.hatenablog.com/entry/2020/01/24/183247 )
### 関数の戻り値のテンプレート ### 関数の戻り値のテンプレート
ユーザに戻り値を決定させたい場合に、明示的に指定する箇所を最低限にしたい場合は最初に返り値の型を指定させるようにするとよい ユーザに戻り値を決定させたい場合に、明示的に指定する箇所を最低限にしたい場合は最初に返り値の型を指定させるようにするとよい
``` cpp ``` cpp
template <typename RET_TYPE, typename T1, typename T2> template <typename RET_TYPE, typename T1, typename T2>
RET_TYPE sum(T1 a, T2 b) { RET_TYPE sum(T1 a, T2 b) {
return a + b; return a + b;
} }
sum<int>(1, 2); sum<int>(1, 2);
``` ```
### 後置の戻り値の型と型変換 ### 後置の戻り値の型と型変換
パラメータが検出されるまで,変数は存在しないため,戻り値の型を後置にしなければならない パラメータが検出されるまで,変数は存在しないため,戻り値の型を後置にしなければならない
``` cpp ``` cpp
template<typename I> template<typename I>
auto func(I beg, I end) -> decltype(*beg) { auto func(I beg, I end) -> decltype(*beg) {
// ... // ...
return *beg; return *beg;
} }
``` ```
## 参照 ## 参照
### `&`付きの型に対して,自由に値を代入したい ### `&`付きの型に対して,自由に値を代入したい
* [C\+\+ \- 参照の初期化を条件分岐で行う方法について|teratail]( https://teratail.com/questions/158884 ) * [C\+\+ \- 参照の初期化を条件分岐で行う方法について|teratail]( https://teratail.com/questions/158884 )
* [std::vector で参照を保持したい \- Secret Garden\(Instrumental\)]( http://secret-garden.hatenablog.com/entry/2015/08/28/000000 ) * [std::vector で参照を保持したい \- Secret Garden\(Instrumental\)]( http://secret-garden.hatenablog.com/entry/2015/08/28/000000 )
``` cpp ``` cpp
#include <functional> #include <functional>
#include <iostream> #include <iostream>
#include <string> #include <string>
#include <vector> #include <vector>
void print_first_one(const std::vector<std::string>& vec) { void print_first_one(const std::vector<std::string>& vec) {
std::cout << vec[0] << std::endl; std::cout << vec[0] << std::endl;
} }
int main(int argc, char* argv[]) { int main(int argc, char* argv[]) {
std::vector<std::string> a = {"alice"}; std::vector<std::string> a = {"alice"};
std::vector<std::string> b = {"bob"}; std::vector<std::string> b = {"bob"};
std::reference_wrapper<std::vector<std::string>> target_ref = a; std::reference_wrapper<std::vector<std::string>> target_ref = a;
std::cout << target_ref.get()[0] << std::endl; std::cout << target_ref.get()[0] << std::endl;
print_first_one(target_ref); print_first_one(target_ref);
target_ref = b; target_ref = b;
std::cout << target_ref.get()[0] << std::endl; std::cout << target_ref.get()[0] << std::endl;
print_first_one(target_ref); print_first_one(target_ref);
return 0; return 0;
} }
``` ```
## new ## new
### placement new ### placement new
[『placement new』自分でメモリを管理してみしょう \| GAMEWORKS LAB]( http://gameworkslab.jp/2020/01/28/%E3%80%8Eplacement-new%E3%80%8F%E8%87%AA%E5%88%86%E3%81%A7%E3%83%A1%E3%83%A2%E3%83%AA%E3%82%92%E7%AE%A1%E7%90%86%E3%81%97%E3%81%A6%E3%81%BF%E3%81%97%E3%82%87%E3%81%86/ ) [『placement new』自分でメモリを管理してみしょう \| GAMEWORKS LAB]( http://gameworkslab.jp/2020/01/28/%E3%80%8Eplacement-new%E3%80%8F%E8%87%AA%E5%88%86%E3%81%A7%E3%83%A1%E3%83%A2%E3%83%AA%E3%82%92%E7%AE%A1%E7%90%86%E3%81%97%E3%81%A6%E3%81%BF%E3%81%97%E3%82%87%E3%81%86/ )
#### コンストラクタは自動的に呼ばれる #### コンストラクタは自動的に呼ばれる
#### デストラクタは明示的に呼び出す必要がある(deleteを呼んではならない) #### デストラクタは明示的に呼び出す必要がある(deleteを呼んではならない)
[c\+\+ \- why destructor is not called implicitly in placement new"? \- Stack Overflow]( https://stackoverflow.com/questions/1022320/why-destructor-is-not-called-implicitly-in-placement-new ) [c\+\+ \- why destructor is not called implicitly in placement new"? \- Stack Overflow]( https://stackoverflow.com/questions/1022320/why-destructor-is-not-called-implicitly-in-placement-new )
## main関数 ## main関数
[implementation-defined]( https://en.cppreference.com/w/cpp/language/main_function ) [implementation-defined]( https://en.cppreference.com/w/cpp/language/main_function )
> A very common implementation-defined form of main() has a third argument (in addition to argc and argv), of type char*[], pointing at an array of pointers to the execution environment variables. > A very common implementation-defined form of main() has a third argument (in addition to argc and argv), of type char*[], pointing at an array of pointers to the execution environment variables.
`envp`は処理系定義 `envp`は処理系定義
``` cpp ``` cpp
int main(int argc, char *argv[], char *envp[]) { } int main(int argc, char *argv[], char *envp[]) { }
``` ```
FYI: [\`main\` function and command-line arguments (C++) | Microsoft Docs]( https://docs.microsoft.com/en-us/cpp/cpp/main-function-command-line-args?view=msvc-170#the-envp-command-line-argument ) FYI: [\`main\` function and command-line arguments (C++) | Microsoft Docs]( https://docs.microsoft.com/en-us/cpp/cpp/main-function-command-line-args?view=msvc-170#the-envp-command-line-argument )
## デバッグ ## デバッグ
### `__PRETTY_FUNCTION__` ### `__PRETTY_FUNCTION__`
[事前定義識別子\_\_func\_\_ \- cpprefjp C\+\+日本語リファレンス]( https://cpprefjp.github.io/lang/cpp11/func.html ) [事前定義識別子\_\_func\_\_ \- cpprefjp C\+\+日本語リファレンス]( https://cpprefjp.github.io/lang/cpp11/func.html )
GCCの言語拡張で、名前空間名、クラス名、戻り値やパラメータといった情報も含む関数の文字列となる GCCの言語拡張で、名前空間名、クラス名、戻り値やパラメータといった情報も含む関数の文字列となる
## std::offstream ## std::offstream
### error handling ### error handling
* [c\+\+ \- Error handling in std::ofstream while writing data \- Stack Overflow]( https://stackoverflow.com/questions/28342660/error-handling-in-stdofstream-while-writing-data ) * [c\+\+ \- Error handling in std::ofstream while writing data \- Stack Overflow]( https://stackoverflow.com/questions/28342660/error-handling-in-stdofstream-while-writing-data )
* [c\+\+ \- Get std::fstream failure error messages and/or exceptions \- Stack Overflow]( https://stackoverflow.com/questions/839644/get-stdfstream-failure-error-messages-and-or-exceptions ) * [c\+\+ \- Get std::fstream failure error messages and/or exceptions \- Stack Overflow]( https://stackoverflow.com/questions/839644/get-stdfstream-failure-error-messages-and-or-exceptions )
__`write`したあとに`flush`して、初めて書き込みが実際に行われて、エラーがどうかがわかる__ __`write`したあとに`flush`して、初めて書き込みが実際に行われて、エラーがどうかがわかる__
例えば、`cgroup``cpu`への`tasks`ファイルへ書き込む際には、書き込むPID,TIDの設定(e.g. スケジューリング)に依存して、エラーとなる可能性があり、書き込む内容に依存した結果となる 例えば、`cgroup``cpu`への`tasks`ファイルへ書き込む際には、書き込むPID,TIDの設定(e.g. スケジューリング)に依存して、エラーとなる可能性があり、書き込む内容に依存した結果となる
## `std::vector` ## `std::vector`
### 非ゼロ値で初期化したい ### 非ゼロ値で初期化したい
``` cpp ``` cpp
std::vector<int> vec = {1, 2, 3}; std::vector<int> vec = {1, 2, 3};
vec.resize(5, 10); vec.resize(5, 10);
``` ```
`{1, 2, 3, 10, 10}` `{1, 2, 3, 10, 10}`
### 確保したバッファを削除したい ### 確保したバッファを削除したい
`clear()``resize(0)`もキャパシティはそのまま確保された状態になるので、`shrink_to_fit()`で切り詰めないと意図した動作にはならない `clear()``resize(0)`もキャパシティはそのまま確保された状態になるので、`shrink_to_fit()`で切り詰めないと意図した動作にはならない
### std::remove_if ### std::remove_if
[remove\_if \- cpprefjp C\+\+日本語リファレンス]( https://cpprefjp.github.io/reference/algorithm/remove_if.html ) [remove\_if \- cpprefjp C\+\+日本語リファレンス]( https://cpprefjp.github.io/reference/algorithm/remove_if.html )
削除する要素を詰めて、サイズが縮んだコンテナの新しい終端イテレータを返す仕様であり、要素は削除されないので、サイズは変わらない 削除する要素を詰めて、サイズが縮んだコンテナの新しい終端イテレータを返す仕様であり、要素は削除されないので、サイズは変わらない
### コピーしたい場合 ### コピーしたい場合
[std vector C\+\+ \-\- deep or shallow copy \- Stack Overflow]( https://stackoverflow.com/questions/11348376/std-vector-c-deep-or-shallow-copy ) [std vector C\+\+ \-\- deep or shallow copy \- Stack Overflow]( https://stackoverflow.com/questions/11348376/std-vector-c-deep-or-shallow-copy )
* `=`: 各要素の値をdeep copyする * `=`: 各要素の値をdeep copyする
* もし、要素自身がポインタならば意味的にはshallow copyとなっている * もし、要素自身がポインタならば意味的にはshallow copyとなっている
* 型が異なる要素のコピーを行う場合は、`std::copy`を利用する * 型が異なる要素のコピーを行う場合は、`std::copy`を利用する
``` cpp ``` cpp
std::vector<uint32_t> vec;// dst std::vector<uint32_t> vec;// dst
std::vector<int> vec_tmp;// src std::vector<int> vec_tmp;// src
vec.resize(vec_tmp.size()); vec.resize(vec_tmp.size());
std::copy(std::begin(vec_tmp), std::end(vec_tmp), std::begin(vec)); std::copy(std::begin(vec_tmp), std::end(vec_tmp), std::begin(vec));
``` ```
### `std::vector`を関数の引数にそのまま指定できるかどうか ### `std::vector`を関数の引数にそのまま指定できるかどうか
``` cpp ``` cpp
#include <vector> #include <vector>
void f_vec_ref(std::vector<int>& vec) {} void f_vec_ref(std::vector<int>& vec) {}
void f_vec(std::vector<int> vec) {} void f_vec(std::vector<int> vec) {}
int main(int argc, char* argv[]) { int main(int argc, char* argv[]) {
std::vector<int> vec; std::vector<int> vec;
vec = {0, 1, 2, 3, 4}; vec = {0, 1, 2, 3, 4};
f_vec(vec); f_vec(vec);
f_vec({0, 1, 2, 3, 4}); f_vec({0, 1, 2, 3, 4});
f_vec_ref(vec); f_vec_ref(vec);
// NG // NG
// f_vec_ref({0, 1, 2, 3, 4}); // f_vec_ref({0, 1, 2, 3, 4});
return 0; return 0;
} }
``` ```
### `std::vector<bool>` ### `std::vector<bool>`
[On vector\<bool> \-\- Howard Hinnant : Standard C\+\+]( https://isocpp.org/blog/2012/11/on-vectorbool ) [On vector\<bool> \-\- Howard Hinnant : Standard C\+\+]( https://isocpp.org/blog/2012/11/on-vectorbool )
* __自動でビットを利用するように最適化される__(内部的にはアロケータとして`std::allocator<bool>`が利用されている) * __自動でビットを利用するように最適化される__(内部的にはアロケータとして`std::allocator<bool>`が利用されている)
* 要素のポインターを取得できない * 要素のポインターを取得できない
* `.data()`メソッドが実装されていない * `.data()`メソッドが実装されていない
``` cpp ``` cpp
std::vector<bool> v(8); std::vector<bool> v(8);
bool* p = &v[0]; bool* p = &v[0];
``` ```
``` ```
error: no viable conversion from '__bit_iterator<std::__1::vector<bool, error: no viable conversion from '__bit_iterator<std::__1::vector<bool,
std::__1::allocator<bool> >, false>' to 'bool *' std::__1::allocator<bool> >, false>' to 'bool *'
``` ```
結局の所,イテレータでのアクセスを前提として利用する形式なので,`int64_t`などの型を利用して効率的に演算は正攻法ではできない 結局の所,イテレータでのアクセスを前提として利用する形式なので,`int64_t`などの型を利用して効率的に演算は正攻法ではできない
サイズ固定ではない`std::bitset`が欲しい場合には`boost::dynamic_bitset`を利用することになりそう サイズ固定ではない`std::bitset`が欲しい場合には`boost::dynamic_bitset`を利用することになりそう
`boost::dynamic_bitset<uint8_t> bit_data;`として,`bit_data.append(0xFF)`とすれば,規定サイズ単位の数値は簡単に追加できる(上位bit方向(先頭)に追加されることに注意) `boost::dynamic_bitset<uint8_t> bit_data;`として,`bit_data.append(0xFF)`とすれば,規定サイズ単位の数値は簡単に追加できる(上位bit方向(先頭)に追加されることに注意)
逆に,`10bit`単位の数を上記のように作成することはできなさそう 逆に,`10bit`単位の数を上記のように作成することはできなさそう
### [std::vector をがんばって高速に resize する \- Qiita]( https://qiita.com/i_saint/items/59c394a28a5244ec94e1 ) ### [std::vector をがんばって高速に resize する \- Qiita]( https://qiita.com/i_saint/items/59c394a28a5244ec94e1 )
## std::regex ## std::regex
### 注意点 ### 注意点
``` cpp ``` cpp
if (std::regex_match(std::string("str[i]"), match, re)) {} if (std::regex_match(std::string("str[i]"), match, re)) {}
``` ```
第1引数はmatchで値を取り出すまで,スコープが有効でなければならないので,上記の例はNG 第1引数はmatchで値を取り出すまで,スコープが有効でなければならないので,上記の例はNG
[regex\_match \- cpprefjp C\+\+日本語リファレンス]( https://cpprefjp.github.io/reference/regex/regex_match.html ) [regex\_match \- cpprefjp C\+\+日本語リファレンス]( https://cpprefjp.github.io/reference/regex/regex_match.html )
> match_results オブジェクトを引数に取る形式の場合、そのオブジェクトは引数で指定した検索対象文字列へのイテレータを保持する。 > match_results オブジェクトを引数に取る形式の場合、そのオブジェクトは引数で指定した検索対象文字列へのイテレータを保持する。
このため、検索対象文字列は本関数を呼び出した後も match_results オブジェクトを使用し終わるまで破棄されないようにする必要がある。 このため、検索対象文字列は本関数を呼び出した後も match_results オブジェクトを使用し終わるまで破棄されないようにする必要がある。
## std::shared_ptr ## std::shared_ptr
### std::shared_ptrでラップしても、基底クラスに継承クラスを代入可能である ### std::shared_ptrでラップしても、基底クラスに継承クラスを代入可能である
### コピー代入ではなくstd::moveを利用するとパフォーマンスが改善できる ### コピー代入ではなくstd::moveを利用するとパフォーマンスが改善できる
[shared_ptr - cpprefjp C++日本語リファレンス]( https://cpprefjp.github.io/reference/memory/shared_ptr.html ) [shared_ptr - cpprefjp C++日本語リファレンス]( https://cpprefjp.github.io/reference/memory/shared_ptr.html )
> 非スレッドセーフに参照カウントを増減させる方法はない。シングルスレッドでのパフォーマンスが重要で、スレッドセーフであることによるオーバーヘッドが問題になる場合、ムーブを活用すればパフォーマンスを改善できる。 > 非スレッドセーフに参照カウントを増減させる方法はない。シングルスレッドでのパフォーマンスが重要で、スレッドセーフであることによるオーバーヘッドが問題になる場合、ムーブを活用すればパフォーマンスを改善できる。
``` diff ``` diff
- piyo = hoge_shared_ptr; - piyo = hoge_shared_ptr;
+ piyo = std::move(hoge_shared_ptr); + piyo = std::move(hoge_shared_ptr);
``` ```
### ラムダ関数との組み合わせの注意点 ### ラムダ関数との組み合わせの注意点
[cpp\-examples/pitfalls/shared\_ptr at master · umaumax/cpp\-examples]( https://github.com/umaumax/cpp-examples/tree/master/pitfalls/shared_ptr ) [cpp\-examples/pitfalls/shared\_ptr at master · umaumax/cpp\-examples]( https://github.com/umaumax/cpp-examples/tree/master/pitfalls/shared_ptr )
[enable_shared_from_this - cpprefjp C++日本語リファレンス]( https://cpprefjp.github.io/reference/memory/enable_shared_from_this.html )に関連するネタ? [enable_shared_from_this - cpprefjp C++日本語リファレンス]( https://cpprefjp.github.io/reference/memory/enable_shared_from_this.html )に関連するネタ?
## `std::execution` ## `std::execution`
[実行ポリシー \- cpprefjp C\+\+日本語リファレンス]( https://cpprefjp.github.io/reference/execution/execution/execution_policy.html ) [実行ポリシー \- cpprefjp C\+\+日本語リファレンス]( https://cpprefjp.github.io/reference/execution/execution/execution_policy.html )
`C++17`から有効な機能 `C++17`から有効な機能
例えば、`std::sort`などを並列で実行することができる 例えば、`std::sort`などを並列で実行することができる
## `std::this_thread::yield` ## `std::this_thread::yield`
ビジーウェイトとする場合にはこれを呼び出して明示的にCPUを明け渡すと行儀が良い ビジーウェイトとする場合にはこれを呼び出して明示的にCPUを明け渡すと行儀が良い
* [yield \- cpprefjp C\+\+日本語リファレンス]( https://cpprefjp.github.io/reference/thread/this_thread/yield.html ) * [yield \- cpprefjp C\+\+日本語リファレンス]( https://cpprefjp.github.io/reference/thread/this_thread/yield.html )
疑問点: 通常の`sleep()`[std::this\_thread::yield \- cppreference\.com]( https://en.cppreference.com/w/cpp/thread/yield )の例のようなsleepのどちらがよい?(ケース・バイ・ケースだと思われるが,短い時間のsleepならば後者こちらの方が明示的にCPUを明け渡せるのでよいのかも) 疑問点: 通常の`sleep()`[std::this\_thread::yield \- cppreference\.com]( https://en.cppreference.com/w/cpp/thread/yield )の例のようなsleepのどちらがよい?(ケース・バイ・ケースだと思われるが,短い時間のsleepならば後者こちらの方が明示的にCPUを明け渡せるのでよいのかも)
## `std::cout` ## `std::cout`
### `std::cout`を`std::ostream`として扱いたい ### `std::cout`を`std::ostream`として扱いたい
[c\+\+11 \- std::ostream to file or standard output \- Stack Overflow]( https://stackoverflow.com/questions/23345504/stdostream-to-file-or-standard-output ) [c\+\+11 \- std::ostream to file or standard output \- Stack Overflow]( https://stackoverflow.com/questions/23345504/stdostream-to-file-or-standard-output )
``` cpp ``` cpp
std::ostream(std::cout.rdbuf()) std::ostream(std::cout.rdbuf())
``` ```
### [std::cout 乗っ取り計画 \- Qiita]( https://qiita.com/yohhoy/items/1dce1c0d19baae48ae78 ) ### [std::cout 乗っ取り計画 \- Qiita]( https://qiita.com/yohhoy/items/1dce1c0d19baae48ae78 )
`std::cout.rdbuf()`を取得して、カスタムしたものを再度、設定する手順を踏む `std::cout.rdbuf()`を取得して、カスタムしたものを再度、設定する手順を踏む
## std::chrono ## std::chrono
### std::chrono::duration ### std::chrono::duration
`std::chrono::milliseconds(duration)``duration`の単位に`double`を利用するとわかりにくいビルドエラーとなることに注意 `std::chrono::milliseconds(duration)``duration`の単位に`double`を利用するとわかりにくいビルドエラーとなることに注意
### std::chrono::system_clock::time_point ### std::chrono::system_clock::time_point
``` cpp ``` cpp
#include <chrono> #include <chrono>
#include <iostream> #include <iostream>
int main(int argc, char* argv[]) { int main(int argc, char* argv[]) {
std::chrono::system_clock::time_point zero{}; std::chrono::system_clock::time_point zero{};
std::chrono::system_clock::time_point min = std::chrono::system_clock::time_point min =
std::chrono::system_clock::time_point::min(); std::chrono::system_clock::time_point::min();
std::chrono::system_clock::time_point now = std::chrono::system_clock::now(); std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
double sub = double sub =
std::chrono::duration_cast<std::chrono::milliseconds>(now - zero).count(); std::chrono::duration_cast<std::chrono::milliseconds>(now - zero).count();
std::cout << "zero:" << zero.time_since_epoch().count() << std::endl; std::cout << "zero:" << zero.time_since_epoch().count() << std::endl;
std::cout << "min:" << min.time_since_epoch().count() << std::endl; std::cout << "min:" << min.time_since_epoch().count() << std::endl;
std::cout << "now:" << now.time_since_epoch().count() << std::endl; std::cout << "now:" << now.time_since_epoch().count() << std::endl;
std::cout << "sub:" << sub << std::endl; std::cout << "sub:" << sub << std::endl;
return 0; return 0;
} }
// zero:0 // zero:0
// min:-9223372036854775808 // min:-9223372036854775808
// now:1589511568463908655 // now:1589511568463908655
// sub:1.58951e+12 // sub:1.58951e+12
``` ```
### `std::chrono::system_clock::now()` ### `std::chrono::system_clock::now()`
実装で最終的に呼び出される`clock_gettime`は発行CPUに関わらず、共通の結果となるように見える 実装で最終的に呼び出される`clock_gettime`は発行CPUに関わらず、共通の結果となるように見える
[c\+\+ \- Where does the time from \`std::chrono::system\_clock::now\(\)\.time\_since\_epoch\(\)\` come from and can it block if accessed from multiple threads? \- Stack Overflow]( https://stackoverflow.com/questions/46740302/where-does-the-time-from-stdchronosystem-clocknow-time-since-epoch-c/46740824#46740824 ) [c\+\+ \- Where does the time from \`std::chrono::system\_clock::now\(\)\.time\_since\_epoch\(\)\` come from and can it block if accessed from multiple threads? \- Stack Overflow]( https://stackoverflow.com/questions/46740302/where-does-the-time-from-stdchronosystem-clocknow-time-since-epoch-c/46740824#46740824 )
> A typical C++ standard library implementation would rely on the underlying OS system call to get the actual system clock value to construct the time_point object. > A typical C++ standard library implementation would rely on the underlying OS system call to get the actual system clock value to construct the time_point object.
[gcc/chrono\.cc at master · gcc\-mirror/gcc]( https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/src/c%2B%2B11/chrono.cc#L80 ) [gcc/chrono\.cc at master · gcc\-mirror/gcc]( https://github.com/gcc-mirror/gcc/blob/master/libstdc%2B%2B-v3/src/c%2B%2B11/chrono.cc#L80 )
> syscall(SYS_clock_gettime, CLOCK_MONOTONIC, &tp); > syscall(SYS_clock_gettime, CLOCK_MONOTONIC, &tp);
> clock_gettime(CLOCK_MONOTONIC, &tp); > clock_gettime(CLOCK_MONOTONIC, &tp);
[linuxで時刻を取得する方法 \- pyopyopyo \- Linuxとかプログラミングの覚え書き \-]( https://pyopyopyo.hatenablog.com/entry/20060711/p1 ) [linuxで時刻を取得する方法 \- pyopyopyo \- Linuxとかプログラミングの覚え書き \-]( https://pyopyopyo.hatenablog.com/entry/20060711/p1 )
各アーキテクチャでの実装例はGolangの実装を見ると良さそう 各アーキテクチャでの実装例はGolangの実装を見ると良さそう
[Goとrdtscの謎を追う \- Qiita]( https://qiita.com/kubo39/items/4319fa243fd18acc0981 ) [Goとrdtscの謎を追う \- Qiita]( https://qiita.com/kubo39/items/4319fa243fd18acc0981 )
[Man page of CLOCK\_GETRES]( https://linuxjm.osdn.jp/html/LDP_man-pages/man2/clock_getres.2.html ) [Man page of CLOCK\_GETRES]( https://linuxjm.osdn.jp/html/LDP_man-pages/man2/clock_getres.2.html )
> CLOCK_PROCESS_CPUTIME_ID (Linux 2.6.12 以降) > CLOCK_PROCESS_CPUTIME_ID (Linux 2.6.12 以降)
> プロセス単位の CPU タイムクロック (そのプロセスの全スレッドで消費される CPU 時間を計測する)。 > プロセス単位の CPU タイムクロック (そのプロセスの全スレッドで消費される CPU 時間を計測する)。
> CLOCK_THREAD_CPUTIME_ID (Linux 2.6.12 以降) > CLOCK_THREAD_CPUTIME_ID (Linux 2.6.12 以降)
> スレッド固有の CPU タイムクロック。 > スレッド固有の CPU タイムクロック。
`CLOCK_MONOTONIC`では、基本的にCPU間の問題は回避できているように見える `CLOCK_MONOTONIC`では、基本的にCPU間の問題は回避できているように見える
## std::map, std::unordered_map ## std::map, std::unordered_map
### `[]`アクセス ### `[]`アクセス
存在する場合はその値を、存在しない場合はデフォルト値(デフォルトコンストラクタが存在する場合)を返すことに注意 存在する場合はその値を、存在しない場合はデフォルト値(デフォルトコンストラクタが存在する場合)を返すことに注意
### `insert()` ### `insert()`
存在しない場合は追加、存在する場合は無視という挙動となることに注意 存在しない場合は追加、存在する場合は無視という挙動となることに注意
### [mapのキーにvectorが使える \- minus9d's diary]( https://minus9d.hatenablog.com/entry/20120610/1339332308 ) ### [mapのキーにvectorが使える \- minus9d's diary]( https://minus9d.hatenablog.com/entry/20120610/1339332308 )
`比較演算子`がある必要がある `比較演算子`がある必要がある
### [俺のunordered\_mapがこんなにpair型をキーにできないわけがない \- Qiita]( https://qiita.com/ganyariya/items/df35d253726269bda436 ) ### [俺のunordered\_mapがこんなにpair型をキーにできないわけがない \- Qiita]( https://qiita.com/ganyariya/items/df35d253726269bda436 )
`ハッシュ可能`である必要がある(第3項目に自作ハッシュ関数を定義すれば良い) `ハッシュ可能`である必要がある(第3項目に自作ハッシュ関数を定義すれば良い)
## `std::stol` ## 文字列からの数値変換 - `std::stol`
* c++ * c++
* [stol - cpprefjp C++日本語リファレンス]( https://cpprefjp.github.io/reference/string/stol.html ) * [stol - cpprefjp C++日本語リファレンス]( https://cpprefjp.github.io/reference/string/stol.html )
* 例外を投げる * 例外を投げる
* c * > パラメータ idx が非 nullptr の場合、変換に使用されなかった要素のインデックス( end - str.c_str() )が格納される。
* [Man page of STRTOL]( https://linuxjm.osdn.jp/html/LDP_man-pages/man3/strtol.3.html ) * 先頭が数字であれば例外は投げないため、この引数を指定することで、与えた文字列がすべて適切に利用されたかどうかを判定できる
* 例外を投げない * c
* [Man page of STRTOL]( https://linuxjm.osdn.jp/html/LDP_man-pages/man3/strtol.3.html )
## `std::function` * 例外を投げない
[std::function のコスト \- Kludge Factory]( https://tyfkda.github.io/blog/2020/03/04/std-function-runtime.html ) ## `std::function`
### `std::function::target()` [std::function のコスト \- Kludge Factory]( https://tyfkda.github.io/blog/2020/03/04/std-function-runtime.html )
[本の虫: std::functionのtargetの使い方]( https://cpplover.blogspot.com/2010/11/stdfunctiontarget.html ) ### `std::function::target()`
> std::function経由で呼び出すには、格納されている型を実行時に判断しなければならない。そのため、すでに格納されている型が分かっているのならば、中身を取り出すことで、そのような実行時チェックを、中身を取り出す際の一回で済ませることができる。 [本の虫: std::functionのtargetの使い方]( https://cpplover.blogspot.com/2010/11/stdfunctiontarget.html )
``` cpp > std::function経由で呼び出すには、格納されている型を実行時に判断しなければならない。そのため、すでに格納されている型が分かっているのならば、中身を取り出すことで、そのような実行時チェックを、中身を取り出す際の一回で済ませることができる。
auto func = *f.target< int (*)(int) >();
for (int i = 0 ; i != 1000000; ++i) { ``` cpp
func(i); auto func = *f.target< int (*)(int) >();
} for (int i = 0 ; i != 1000000; ++i) {
``` func(i);
}
[関数ポインタ型を混在させて運用する \- C\+\+ プログラミング]( https://ez-net.jp/article/6A/su9HMTVF/wKanc4EjbZFS/ ) ```
ラムダ関数を`target()`で取り出すうまい方法がない [関数ポインタ型を混在させて運用する \- C\+\+ プログラミング]( https://ez-net.jp/article/6A/su9HMTVF/wKanc4EjbZFS/ )
#### `std::function`の中身が指している関数の同一性の比較 ラムダ関数を`target()`で取り出すうまい方法がない
直接、`==`を利用して比較してはならず、`target()`を経由する #### `std::function`の中身が指している関数の同一性の比較
基本的に`target()`にて、 __正しい型__ を指定できていることが前提なので,下記のような比較ではなく,どの関数を代入しているかどうかのメタ情報を管理するほうがメンテナンス性がよいと考えられる(C関数ポインタであるか,ラムダ関数であるか,`std::bind`を利用しているかなどによって,この型が変わってしまう) 直接、`==`を利用して比較してはならず、`target()`を経由する
``` cpp 基本的に`target()`にて、 __正しい型__ を指定できていることが前提なので,下記のような比較ではなく,どの関数を代入しているかどうかのメタ情報を管理するほうがメンテナンス性がよいと考えられる(C関数ポインタであるか,ラムダ関数であるか,`std::bind`を利用しているかなどによって,この型が変わってしまう)
#include <cassert>
#include <functional> ``` cpp
#include <iostream> #include <cassert>
#include <functional>
void f() { std::cout << "f" << std::endl; } #include <iostream>
void g() { std::cout << "g" << std::endl; }
void f() { std::cout << "f" << std::endl; }
int main(int argc, char const* argv[]) { void g() { std::cout << "g" << std::endl; }
std::function<void(void)> func_f1(f); // or &f
std::function<void(void)> func_f2(f); int main(int argc, char const* argv[]) {
std::function<void(void)> func_g(g); std::function<void(void)> func_f1(f); // or &f
std::function<void(void)> func_lambda( std::function<void(void)> func_f2(f);
[]() { std::cout << "lambda" << std::endl; }); std::function<void(void)> func_g(g);
std::function<void(void)> func_lambda(
// NOTE: target() return the pointer to function pointer []() { std::cout << "lambda" << std::endl; });
// WARN: if type is not correct, target() return nullptr
assert(func_f1.target<void(void)>() == nullptr); // NOTE: target() return the pointer to function pointer
assert(func_lambda.target<void(void)>() == nullptr); // WARN: if type is not correct, target() return nullptr
assert(func_lambda.target<void (*)(void)>() == nullptr); assert(func_f1.target<void(void)>() == nullptr);
assert(func_lambda.target<void(void)>() == nullptr);
assert(func_f1.target<void (*)(void)>() != nullptr); assert(func_lambda.target<void (*)(void)>() == nullptr);
assert(func_f2.target<void (*)(void)>() != nullptr);
assert(func_g.target<void (*)(void)>() != nullptr); assert(func_f1.target<void (*)(void)>() != nullptr);
assert(func_f2.target<void (*)(void)>() != nullptr);
auto func_f1_p = *func_f1.target<void (*)(void)>(); assert(func_g.target<void (*)(void)>() != nullptr);
auto func_f2_p = *func_f2.target<void (*)(void)>();
auto func_g_p = *func_g.target<void (*)(void)>(); auto func_f1_p = *func_f1.target<void (*)(void)>();
auto func_f2_p = *func_f2.target<void (*)(void)>();
printf("f:%p\n", &f); auto func_g_p = *func_g.target<void (*)(void)>();
printf("g:%p\n", &g);
printf("func_f1:%p\n", func_f1_p); printf("f:%p\n", &f);
printf("func_f2:%p\n", func_f2_p); printf("g:%p\n", &g);
printf("func_g:%p\n", func_g_p); printf("func_f1:%p\n", func_f1_p);
printf("func_f2:%p\n", func_f2_p);
assert(func_f1_p == func_f2_p); printf("func_g:%p\n", func_g_p);
assert(func_f1_p == &f);
assert(func_g_p == &g); assert(func_f1_p == func_f2_p);
assert(func_f1_p == &f);
func_f1(); assert(func_g_p == &g);
func_f2();
func_g(); func_f1();
func_lambda(); func_f2();
return 0; func_g();
} func_lambda();
``` return 0;
}
## algorithm header ```
[mismatch - cpprefjp C++日本語リファレンス]( https://cpprefjp.github.io/reference/algorithm/mismatch.html )
## algorithm header
## glibc [mismatch - cpprefjp C++日本語リファレンス]( https://cpprefjp.github.io/reference/algorithm/mismatch.html )
### glibcのソースコードの検索ページ
[bminor/glibc: Unofficial mirror of sourceware glibc repository\. Updated daily\.]( https://github.com/bminor/glibc ) ## glibc
### glibcのソースコードの検索ページ
## ヘッダ [bminor/glibc: Unofficial mirror of sourceware glibc repository\. Updated daily\.]( https://github.com/bminor/glibc )
### unistd.h
UNIx STanDard Header file ## ヘッダ
### unistd.h
### ヘッダファイルはヘッダとソースのどちらでincludeするべき? UNIx STanDard Header file
* [Google C\+\+ Style Guide - Include_What_You_Use]( https://google.github.io/styleguide/cppguide.html#Include_What_You_Use )
* [c\+\+ \- Including \#includes in header file vs source file \- Stack Overflow]( https://stackoverflow.com/questions/2596449/including-includes-in-header-file-vs-source-file/2596554 ) ### ヘッダファイルはヘッダとソースのどちらでincludeするべき?
* [Google C\+\+ Style Guide - Include_What_You_Use]( https://google.github.io/styleguide/cppguide.html#Include_What_You_Use )
必要なファイルでincludeするべきである * [c\+\+ \- Including \#includes in header file vs source file \- Stack Overflow]( https://stackoverflow.com/questions/2596449/including-includes-in-header-file-vs-source-file/2596554 )
理由の一つとしては、そのヘッダをincludeしたときに実装のみで必要となるヘッダも余計にincludeしてしまい、無駄であるから 必要なファイルでincludeするべきである
### 余計なヘッダファイルの見つけ方 理由の一つとしては、そのヘッダをincludeしたときに実装のみで必要となるヘッダも余計にincludeしてしまい、無駄であるから
余計と思われるヘッダを減らした際に`-c`オプション付きでビルドできるかどうかを基準とする方法がある
### 余計なヘッダファイルの見つけ方
* [include\-what\-you\-use/include\-what\-you\-use: A tool for use with clang to analyze \#includes in C and C\+\+ source files]( https://github.com/include-what-you-use/include-what-you-use ) 余計と思われるヘッダを減らした際に`-c`オプション付きでビルドできるかどうかを基準とする方法がある
* [include\-what\-you\-useとjenkinsでC/C\+\+プロジェクトから不要な\#includeを洗い出す \- Qiita]( https://qiita.com/tomota-tar-gz/items/985b660e8f3052a387ef )
* [include\-what\-you\-use/include\-what\-you\-use: A tool for use with clang to analyze \#includes in C and C\+\+ source files]( https://github.com/include-what-you-use/include-what-you-use )
## 初期化 * [include\-what\-you\-useとjenkinsでC/C\+\+プロジェクトから不要な\#includeを洗い出す \- Qiita]( https://qiita.com/tomota-tar-gz/items/985b660e8f3052a387ef )
* [C言語 未初期化変数の罠 \- 気まま研究所ブログ]( https://aonasuzutsuki.hatenablog.jp/entry/2018/12/21/111450 )
* [変数の初期化をサボるな,それから \-Wshadow オプションを使え \- akihiko’s tech note]( https://aki-yam.hatenablog.com/entry/20130718/1374163303 ) ## 初期化
* [C言語 未初期化変数の罠 \- 気まま研究所ブログ]( https://aonasuzutsuki.hatenablog.jp/entry/2018/12/21/111450 )
### 複雑な処理をした結果のconst初期化 * [変数の初期化をサボるな,それから \-Wshadow オプションを使え \- akihiko’s tech note]( https://aki-yam.hatenablog.com/entry/20130718/1374163303 )
[C++ Core Guidelines]( https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#es28-use-lambdas-for-complex-initialization-especially-of-const-variables )
### 複雑な処理をした結果のconst初期化
e.g. [C++ Core Guidelines]( https://isocpp.github.io/CppCoreGuidelines/CppCoreGuidelines#es28-use-lambdas-for-complex-initialization-especially-of-const-variables )
``` cpp
const widget x = [&] { e.g.
widget val; // assume that widget has a default constructor ``` cpp
for (auto i = 2; i <= N; ++i) { // this could be some const widget x = [&] {
val += some_obj.do_something_with(i); // arbitrarily long code widget val; // assume that widget has a default constructor
} // needed to initialize x for (auto i = 2; i <= N; ++i) { // this could be some
return val; val += some_obj.do_something_with(i); // arbitrarily long code
}(); } // needed to initialize x
``` return val;
}();
### 配列の初期化 ```
``` cpp
#include <array> ### 配列の初期化
#include <iostream> ``` cpp
#include <string> #include <array>
#include <iostream>
int main(int argc, char* argv[]) { #include <string>
std::array<int, 800> q; // NG
// std::array<int, 800> q = {}; // OK int main(int argc, char* argv[]) {
for (int i = 0; i < 800; i++) { std::array<int, 800> q; // NG
if (q[i] != 0) { // std::array<int, 800> q = {}; // OK
std::cout << q[i] << std::endl; for (int i = 0; i < 800; i++) {
} if (q[i] != 0) {
} std::cout << q[i] << std::endl;
// initialize by fill }
std::fill(q.begin(), q.end(), 0); }
return 0; // initialize by fill
} std::fill(q.begin(), q.end(), 0);
``` return 0;
}
デフォルトで不定値 ```
``` bash
1920169263 デフォルトで不定値
1651076143 ``` bash
1819894831 1920169263
100 1651076143
32 1819894831
2 100
4 32
4 2
2 4
``` 4
2
[Array initialization \- cppreference\.com]( https://en.cppreference.com/w/c/language/array_initialization ) ```
> In C, the braced list of an initializer cannot be empty. C++ allows empty list: [Array initialization \- cppreference\.com]( https://en.cppreference.com/w/c/language/array_initialization )
> int a[3] = {0}; // valid C and C++ way to zero-out a block-scope array
> int a[3] = {}; // invalid C but valid C++ way to zero-out a block-scope array > In C, the braced list of an initializer cannot be empty. C++ allows empty list:
> int a[3] = {0}; // valid C and C++ way to zero-out a block-scope array
Macで試すとCでも問題なくビルドできた > int a[3] = {}; // invalid C but valid C++ way to zero-out a block-scope array
### structの初期化方法 Macで試すとCでも問題なくビルドできた
[C\+\+における構造体の初期化 \| 株式会社きじねこ]( http://www.kijineko.co.jp/node/681 )
### structの初期化方法
`Hoge hoge = {};`でOK [C\+\+における構造体の初期化 \| 株式会社きじねこ]( http://www.kijineko.co.jp/node/681 )
``` cpp `Hoge hoge = {};`でOK
struct A
{ ``` cpp
int a; struct A
int b; {
}; int a;
A a = { }; int b;
``` };
A a = { };
> のようにすれば、すべてのデータメンバをゼロ初期化または省略時初期化(データメンバが利用者定義のコ<ンストラクタを持つクラスの場合)することができます。 ```
> Cの場合は、{ } の中に少なくともひとつの初期値を記述しなければなりませんが、C++ではまったく書かなくてもOKです。
> のようにすれば、すべてのデータメンバをゼロ初期化または省略時初期化(データメンバが利用者定義のコ<ンストラクタを持つクラスの場合)することができます。
> { } による初期化にせよ、( ) による初期化にせよ、処理系によっては内部的にmemsetを呼び出していることも少なからずあります。 > Cの場合は、{ } の中に少なくともひとつの初期値を記述しなければなりませんが、C++ではまったく書かなくてもOKです。
この場合のように,`memset()`では`double``float`など危険である > { } による初期化にせよ、( ) による初期化にせよ、処理系によっては内部的にmemsetを呼び出していることも少なからずあります。
### memsetで0埋めならばたまたま想定通りになる この場合のように,`memset()`では`double``float`など危険である
[浮動小数点数の内部表現\(IEEE\)]( https://www.k-cube.co.jp/wakaba/server/floating_point.html )
### memsetで0埋めならばたまたま想定通りになる
``` cpp [浮動小数点数の内部表現\(IEEE\)]( https://www.k-cube.co.jp/wakaba/server/floating_point.html )
数値:0.0
d = 0.000000 ``` cpp
00 00 00 00 00 00 00 00 数値:0.0
f = 0.000000 d = 0.000000
00 00 00 00 00 00 00 00 00 00 00 00
``` f = 0.000000
00 00 00 00
IEEE 754の規格という前提 ```
### newで初期化子が利用できる IEEE 754の規格という前提
[初期化子リスト \- cpprefjp C\+\+日本語リファレンス]( https://cpprefjp.github.io/lang/cpp11/initializer_lists.html ) ### newで初期化子が利用できる
e.g. [初期化子リスト \- cpprefjp C\+\+日本語リファレンス]( https://cpprefjp.github.io/lang/cpp11/initializer_lists.html )
``` cpp
#include <iostream> e.g.
#include <string> ``` cpp
#include <iostream>
int main(int argc, char* argv[]) { #include <string>
double* buf = new double[9]{};
for (int i = 0; i < 9; i++) { int main(int argc, char* argv[]) {
std::cout << buf[i] << std::endl; double* buf = new double[9]{};
} for (int i = 0; i < 9; i++) {
return 0; std::cout << buf[i] << std::endl;
} }
``` return 0;
}
### 文字列配列の初期化 ```
[STR11\-C\. 文字列リテラルで初期化される文字配列のサイズを指定しない]( https://www.jpcert.or.jp/sc-rules/c-str11-c.html )
### 文字列配列の初期化
``` cpp [STR11\-C\. 文字列リテラルで初期化される文字配列のサイズを指定しない]( https://www.jpcert.or.jp/sc-rules/c-str11-c.html )
#include <iostream>
#include <string> ``` cpp
#include <iostream>
int main(int argc, char* argv[]) { #include <string>
const char* name_list[] = {"ab", "cd", "ef", "gh", "ij"};
// or int main(int argc, char* argv[]) {
// const char name_list[5][3+1] const char* name_list[] = {"ab", "cd", "ef", "gh", "ij"};
// const char* name_list[5] // or
std::cout << sizeof("ab") << std::endl; // 2+1 // const char name_list[5][3+1]
std::cout << sizeof(char*) << std::endl; // 8(x86_64) // const char* name_list[5]
std::cout << sizeof(name_list) << std::endl; // 5*sizeof(char*)=5*8 std::cout << sizeof("ab") << std::endl; // 2+1
std::cout << sizeof(name_list[0]) << std::endl; // sizeof(char*)=8 std::cout << sizeof(char*) << std::endl; // 8(x86_64)
for (const char* name : name_list) { std::cout << sizeof(name_list) << std::endl; // 5*sizeof(char*)=5*8
std::cout << name << std::endl; std::cout << sizeof(name_list[0]) << std::endl; // sizeof(char*)=8
} for (const char* name : name_list) {
// or use std::string without const std::cout << name << std::endl;
// std::string name_list[] = {"ab", "cd", "ef", "gh", "ij"}; }
return 0; // or use std::string without const
} // std::string name_list[] = {"ab", "cd", "ef", "gh", "ij"};
``` return 0;
}
注意 ```
* [Size of character \('a'\) in C/C\+\+ \- Stack Overflow]( https://stackoverflow.com/questions/2172943/size-of-character-a-in-c-c )
* [sizeof演算子にまつわるアレコレ \- Qiita]( https://qiita.com/yohhoy/items/a2ab2900a2bd36c31879 ) 注意
* [Size of character \('a'\) in C/C\+\+ \- Stack Overflow]( https://stackoverflow.com/questions/2172943/size-of-character-a-in-c-c )
``` cpp * [sizeof演算子にまつわるアレコレ \- Qiita]( https://qiita.com/yohhoy/items/a2ab2900a2bd36c31879 )
const char* ok_list[] = {"ab", "cd", "ef", "gh", "ij"};
class Hoge { ``` cpp
const char* ok_list[5] = {"ab", "cd", "ef", "gh", "ij"}; const char* ok_list[] = {"ab", "cd", "ef", "gh", "ij"};
// error: initializer for flexible array member ‘const char* Hoge::ng_list []’ class Hoge {
const char* ng_list[] = {"ab", "cd", "ef", "gh", "ij"}; const char* ok_list[5] = {"ab", "cd", "ef", "gh", "ij"};
}; // error: initializer for flexible array member ‘const char* Hoge::ng_list []’
``` const char* ng_list[] = {"ab", "cd", "ef", "gh", "ij"};
};
* [c \- How to initialize a structure with flexible array member \- Stack Overflow]( https://stackoverflow.com/questions/8687671/how-to-initialize-a-structure-with-flexible-array-member ) ```
* [DCL38\-C\. フレキシブル配列メンバには正しい構文を使用する]( https://www.jpcert.or.jp/sc-rules/c-dcl38-c.html )
* [フレキシブル配列メンバをC\+\+で \- 茂加部珈琲店]( http://mocabe.hatenablog.com/entry/2018/05/23/121706 ) * [c \- How to initialize a structure with flexible array member \- Stack Overflow]( https://stackoverflow.com/questions/8687671/how-to-initialize-a-structure-with-flexible-array-member )
* [C言語のフレキシブル配列メンバ(flexible array member)、通称struct hack \| NO MORE\! 車輪の再発明]( https://mem-archive.com/2018/08/10/post-529/ ) * [DCL38\-C\. フレキシブル配列メンバには正しい構文を使用する]( https://www.jpcert.or.jp/sc-rules/c-dcl38-c.html )
* あくまでCの構文? * [フレキシブル配列メンバをC\+\+で \- 茂加部珈琲店]( http://mocabe.hatenablog.com/entry/2018/05/23/121706 )
* [C言語のフレキシブル配列メンバ(flexible array member)、通称struct hack \| NO MORE\! 車輪の再発明]( https://mem-archive.com/2018/08/10/post-529/ )
### `std::atomic`の初期値 * あくまでCの構文?
* [c\+\+ \- What's the default value for a std::atomic? \- Stack Overflow]( https://stackoverflow.com/questions/36320008/whats-the-default-value-for-a-stdatomic )
* [atomic::コンストラクタ \- cpprefjp C\+\+日本語リファレンス]( https://cpprefjp.github.io/reference/atomic/atomic/op_constructor.html ) ### `std::atomic`の初期値
* [c\+\+ \- What's the default value for a std::atomic? \- Stack Overflow]( https://stackoverflow.com/questions/36320008/whats-the-default-value-for-a-stdatomic )
* C++17までは不定値 * [atomic::コンストラクタ \- cpprefjp C\+\+日本語リファレンス]( https://cpprefjp.github.io/reference/atomic/atomic/op_constructor.html )
* `{}`で初期化可能
* C++20からは値ゼロで初期化 * C++17までは不定値
* `{}`で初期化可能
### グローバル変数/スレッドローカル変数(ゼロ初期化) * C++20からは値ゼロで初期化
[Zero initialization \- cppreference\.com]( https://en.cppreference.com/w/cpp/language/zero_initialization )
### グローバル変数/スレッドローカル変数(ゼロ初期化)
> For every named variable with static or thread-local storage duration that is not subject to constant initialization, before any other initialization. [Zero initialization \- cppreference\.com]( https://en.cppreference.com/w/cpp/language/zero_initialization )
下記のように、明示的に処理化されていないグローバル変数およびスレッドローカル変数はゼロ初期化されるが、明示的に初期化子を記述したほうがわかりやすい > For every named variable with static or thread-local storage duration that is not subject to constant initialization, before any other initialization.
``` cpp
// global variables 下記のように、明示的に処理化されていないグローバル変数およびスレッドローカル変数はゼロ初期化されるが、明示的に初期化子を記述したほうがわかりやすい
double f[3]; // zero-initialized to three 0.0's ``` cpp
int* p; // zero-initialized to null pointer value // global variables
std::string s; // zero-initialized to indeterminate value then default-initialized to "" double f[3]; // zero-initialized to three 0.0's
``` int* p; // zero-initialized to null pointer value
std::string s; // zero-initialized to indeterminate value then default-initialized to ""
### ローカル変数/クラスメンバー(ゼロ初期化) ```
[Value initialization \(since C\+\+03\) \- cppreference\.com]( https://en.cppreference.com/w/cpp/language/value_initialization ) ### ローカル変数/クラスメンバー(ゼロ初期化)
``` cpp [Value initialization \(since C\+\+03\) \- cppreference\.com]( https://en.cppreference.com/w/cpp/language/value_initialization )
int n{}; // scalar => zero-initialization, the value is 0
double f = double(); // scalar => zero-initialization, the value is 0.0 ``` cpp
int* a = new int[10](); // array => value-initialization of each element the value of each element is 0 int n{}; // scalar => zero-initialization, the value is 0
``` double f = double(); // scalar => zero-initialization, the value is 0.0
int* a = new int[10](); // array => value-initialization of each element the value of each element is 0
デフォルトコンストラクタが実装されていない構造体の場合(明示的にコンストラクタを実装した場合には暗黙的なデフォルトコンストラクタが実装されなくなる)は`{}``()`が利用できなくなる ```
実質、`()`(C++03)と`{}`(C++11)の初期化に違いはないはず... デフォルトコンストラクタが実装されていない構造体の場合(明示的にコンストラクタを実装した場合には暗黙的なデフォルトコンストラクタが実装されなくなる)は`{}``()`が利用できなくなる
`float* ary = new float[1000]();``float* ary = new float[1000]{};`のアセンブリが一致した 実質、`()`(C++03)と`{}`(C++11)の初期化に違いはないはず...
FYI: [Aggregate initialization \- cppreference\.com]( https://en.cppreference.com/w/cpp/language/aggregate_initialization ) `float* ary = new float[1000]();``float* ary = new float[1000]{};`のアセンブリが一致した
[Array initialization \- cppreference\.com]( https://en.cppreference.com/w/c/language/array_initialization ) FYI: [Aggregate initialization \- cppreference\.com]( https://en.cppreference.com/w/cpp/language/aggregate_initialization )
``` cpp [Array initialization \- cppreference\.com]( https://en.cppreference.com/w/c/language/array_initialization )
int a[3] = {0}; // valid C and C++ way to zero-out a block-scope array
int a[3] = {}; // invalid C but valid C++ way to zero-out a block-scope array ``` cpp
``` int a[3] = {0}; // valid C and C++ way to zero-out a block-scope array
int a[3] = {}; // invalid C but valid C++ way to zero-out a block-scope array
### static変数の初期化はスレッドセーフで行われる ```
[ブロックスコープを持つstatic変数初期化のスレッドセーフ化 \- cpprefjp C\+\+日本語リファレンス]( https://cpprefjp.github.io/lang/cpp11/static_initialization_thread_safely.html )
### static変数の初期化はスレッドセーフで行われる
> ブロックスコープを持つstatic変数の初期化は、スレッドセーフであることが規定された。 [ブロックスコープを持つstatic変数初期化のスレッドセーフ化 \- cpprefjp C\+\+日本語リファレンス]( https://cpprefjp.github.io/lang/cpp11/static_initialization_thread_safely.html )
> static変数の初期化が完了するまで、他のスレッドは初期化処理の前で待機する。
> ブロックスコープを持つstatic変数の初期化は、スレッドセーフであることが規定された。
### グローバル変数の初期化順 > static変数の初期化が完了するまで、他のスレッドは初期化処理の前で待機する。
[How do I prevent the “static initialization order problem”?]( https://isocpp.org/wiki/faq/ctors#static-init-order-on-first-use )
### グローバル変数の初期化順
* 仕様で順番は規定されていない [How do I prevent the “static initialization order problem”?]( https://isocpp.org/wiki/faq/ctors#static-init-order-on-first-use )
* 複数ファイルにまたがる場合に、順番を明確にしたい場合は関数経由の呼び出しかgccの拡張機能(`__attribute__((init_priority(101)))`)を利用する
* Mac OS Xのgccでは利用できない * 仕様で順番は規定されていない
* 複数ファイルにまたがる場合に、順番を明確にしたい場合は関数経由の呼び出しかgccの拡張機能(`__attribute__((init_priority(101)))`)を利用する
[\_\_attribute\_\_\)\(\(init\_priority\(N\)\)\)\(で、C\+\+のグローバル変数の初期化順序を制御する \- memologue]( https://yupo5656.hatenadiary.org/entry/20070203/p1 ) * Mac OS Xのgccでは利用できない
> C++の規格では、コンパイル単位をまたいだグローバル変数の初期化順序は規定されていませんので、g++に責任はありません。 [\_\_attribute\_\_\)\(\(init\_priority\(N\)\)\)\(で、C\+\+のグローバル変数の初期化順序を制御する \- memologue]( https://yupo5656.hatenadiary.org/entry/20070203/p1 )
> この初期化順が不定な問題は、init_priority属性を使うと手軽に解決できます。
> C++の規格では、コンパイル単位をまたいだグローバル変数の初期化順序は規定されていませんので、g++に責任はありません。
> init_priority(N)のNの部分の数字が「優先度」で、101以上、65535以下の数値を書けます。小さい数字を指定した変数が先に初期化されるようになります。 > この初期化順が不定な問題は、init_priority属性を使うと手軽に解決できます。
> リンカスクリプトを見る限り、「同じ優先度の変数同士の初期化順は、リンクしてみないとわからない」ことと、スクリプトに KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors)) が先に書かれていることから、「init_priority指定のない変数と、init_priority(65535) の変数では、後者の初期化の方が先に行われる」こともわかります。 > init_priority(N)のNの部分の数字が「優先度」で、101以上、65535以下の数値を書けます。小さい数字を指定した変数が先に初期化されるようになります。
> init_priorityの番号による初期化順の制御は、単一の.so内、および単一の実行ファイル内でのみ効くようですね。 > リンカスクリプトを見る限り、「同じ優先度の変数同士の初期化順は、リンクしてみないとわからない」ことと、スクリプトに KEEP (*(EXCLUDE_FILE (*crtend.o *crtend?.o ) .ctors)) が先に書かれていることから、「init_priority指定のない変数と、init_priority(65535) の変数では、後者の初期化の方が先に行われる」こともわかります。
グローバス変数の次に、スレッドローカルな変数が初期化されることを挙動から確認 > init_priorityの番号による初期化順の制御は、単一の.so内、および単一の実行ファイル内でのみ効くようですね。
``` cpp
// 1 グローバス変数の次に、スレッドローカルな変数が初期化されることを挙動から確認
Hoge hoge; ``` cpp
// 1
// 2 Hoge hoge;
thread_local __attribute__((init_priority(101))) Fuga fuga;
``` // 2
thread_local __attribute__((init_priority(101))) Fuga fuga;
#### グローバル変数の初期化順(実験による観測) ```
どのスコープでも、同一のスコープならば、上の行が優先される
#### グローバル変数の初期化順(実験による観測)
* ファイルスコープ どのスコープでも、同一のスコープならば、上の行が優先される
* `main()`前に初期化される
* `__attribute__((init_priority(101)))`の値が小さい方 * ファイルスコープ
* ファイルスコープTLS * `main()`前に初期化される
* 呼ばれたタイミング(ただし、1つでもファイルスコープTLSが呼ばれたタイミングで、ファイルスコープTLS全体の初期化処理が行われる模様) * `__attribute__((init_priority(101)))`の値が小さい方
* どのファイルスコープTLSが呼ばれたかどうかは関係ない * ファイルスコープTLS
* 明示的に呼び出していない場合には、一切呼び出されないことに注意 * 呼ばれたタイミング(ただし、1つでもファイルスコープTLSが呼ばれたタイミングで、ファイルスコープTLS全体の初期化処理が行われる模様)
* 関数スコープ * どのファイルスコープTLSが呼ばれたかどうかは関係ない
* 呼ばれたタイミング * 明示的に呼び出していない場合には、一切呼び出されないことに注意
* 関数スコープTLS * 関数スコープ
* 呼ばれたタイミング * 呼ばれたタイミング
* 関数スコープTLS
* 関数スコープの値をどうしても早く初期化したい場合は、ファイルスコープからその関数を呼び出せば良い * 呼ばれたタイミング
* 可能な限り、早く値を初期化したい場合には`__attribute__((init_priority(101)))`を付与したファイルスコープの初期化時に、初期化したい対象を利用すればよい
* 関数スコープの値をどうしても早く初期化したい場合は、ファイルスコープからその関数を呼び出せば良い
* `thread_local``__attribute__((init_priority(101)))`を設定可能だが、意味がない * 可能な限り、早く値を初期化したい場合には`__attribute__((init_priority(101)))`を付与したファイルスコープの初期化時に、初期化したい対象を利用すればよい
* `__attribute__((init_priority(101)))`は、クラスに対してかつファイルスコープの変数のみに利用可能
* `thread_local``__attribute__((init_priority(101)))`を設定可能だが、意味がない
なお、macの`g++-11`では、`init_priority`が有効ではなかった * `__attribute__((init_priority(101)))`は、クラスに対してかつファイルスコープの変数のみに利用可能
``` cpp なお、macの`g++-11`では、`init_priority`が有効ではなかった
#include <iostream>
``` cpp
class Hoge { #include <iostream>
public:
Hoge(int input) { v = input; } class Hoge {
int v; public:
}; Hoge(int input) { v = input; }
int v;
int f(int input); };
int v = 0; int f(int input);
// thread_local __attribute__((init_priority(101))) Hoge outer_hoge1(++v); // 1 int v = 0;
// thread_local __attribute__((init_priority(102))) Hoge outer_hoge2(++v); // 2
// thread_local __attribute__((init_priority(103))) Hoge outer_hoge3(++v); // 3 // thread_local __attribute__((init_priority(101))) Hoge outer_hoge1(++v); // 1
// thread_local __attribute__((init_priority(102))) Hoge outer_hoge2(++v); // 2
// thread_local __attribute__((init_priority(103))) Hoge outer_hoge3(++v); // 1 // thread_local __attribute__((init_priority(103))) Hoge outer_hoge3(++v); // 3
// thread_local __attribute__((init_priority(102))) Hoge outer_hoge2(++v); // 2
// thread_local __attribute__((init_priority(101))) Hoge outer_hoge1(++v); // 3 // thread_local __attribute__((init_priority(103))) Hoge outer_hoge3(++v); // 1
// thread_local __attribute__((init_priority(102))) Hoge outer_hoge2(++v); // 2
// __attribute__((init_priority(101))) Hoge outer_hoge1(++v); // 1 // thread_local __attribute__((init_priority(101))) Hoge outer_hoge1(++v); // 3
// __attribute__((init_priority(102))) Hoge outer_hoge2(++v); // 2
// __attribute__((init_priority(103))) Hoge outer_hoge3(++v); // 3 // __attribute__((init_priority(101))) Hoge outer_hoge1(++v); // 1
// __attribute__((init_priority(102))) Hoge outer_hoge2(++v); // 2
// __attribute__((init_priority(103))) Hoge outer_hoge3(++v); // 1 // __attribute__((init_priority(103))) Hoge outer_hoge3(++v); // 3
// __attribute__((init_priority(102))) Hoge outer_hoge2(++v); // 2
// __attribute__((init_priority(101))) Hoge outer_hoge1(++v); // 3 // __attribute__((init_priority(103))) Hoge outer_hoge3(++v); // 1
// __attribute__((init_priority(102))) Hoge outer_hoge2(++v); // 2
// Hoge outer_hoge3(++v); // 1 // __attribute__((init_priority(101))) Hoge outer_hoge1(++v); // 3
// Hoge outer_hoge2(++v); // 2
// Hoge outer_hoge1(++v); // 3 // Hoge outer_hoge3(++v); // 1
// Hoge outer_hoge2(++v); // 2
Hoge outer_hoge1(++v); // 1 // Hoge outer_hoge1(++v); // 3
Hoge outer_hoge2(++v); // 2
Hoge outer_hoge3(++v); // 3 Hoge outer_hoge1(++v); // 1
Hoge outer_hoge2(++v); // 2
int f(int input) { Hoge outer_hoge3(++v); // 3
// can only use ‘init_priority’ attribute on file-scope definitions of objects of class type
// int f(int input) {
// can only use ‘init_priority’ attribute on file-scope definitions of objects of class type
// static thread_local Hoge inner_hoge3(++v); // 1 //
// static thread_local Hoge inner_hoge2(++v); // 2
// static thread_local Hoge inner_hoge1(++v); // 3 // static thread_local Hoge inner_hoge3(++v); // 1
// static thread_local Hoge inner_hoge2(++v); // 2
// static thread_local Hoge inner_hoge1(++v); // 1 // static thread_local Hoge inner_hoge1(++v); // 3
// static thread_local Hoge inner_hoge2(++v); // 2
// static thread_local Hoge inner_hoge3(++v); // 3 // static thread_local Hoge inner_hoge1(++v); // 1
// static thread_local Hoge inner_hoge2(++v); // 2
// static Hoge inner_hoge3(++v); // 1 // static thread_local Hoge inner_hoge3(++v); // 3
// static Hoge inner_hoge2(++v); // 2
// static Hoge inner_hoge1(++v); // 3 // static Hoge inner_hoge3(++v); // 1
// static Hoge inner_hoge2(++v); // 2
static Hoge inner_hoge1(++v); // 1 // static Hoge inner_hoge1(++v); // 3
static Hoge inner_hoge2(++v); // 2
static Hoge inner_hoge3(++v); // 3 static Hoge inner_hoge1(++v); // 1
static Hoge inner_hoge2(++v); // 2
// Hoge inner_hoge1(++v); // 1 static Hoge inner_hoge3(++v); // 3
// Hoge inner_hoge2(++v); // 2
// Hoge inner_hoge3(++v); // 3 // Hoge inner_hoge1(++v); // 1
// Hoge inner_hoge2(++v); // 2
std::cout << "f() outer hoge1:" << outer_hoge1.v << std::endl; // Hoge inner_hoge3(++v); // 3
std::cout << "f() outer hoge2:" << outer_hoge2.v << std::endl;
std::cout << "f() outer hoge3:" << outer_hoge3.v << std::endl; std::cout << "f() outer hoge1:" << outer_hoge1.v << std::endl;
std::cout << "f() outer hoge2:" << outer_hoge2.v << std::endl;
std::cout << "f() inner hoge1:" << inner_hoge1.v << std::endl; std::cout << "f() outer hoge3:" << outer_hoge3.v << std::endl;
std::cout << "f() inner hoge2:" << inner_hoge2.v << std::endl;
std::cout << "f() inner hoge3:" << inner_hoge3.v << std::endl; std::cout << "f() inner hoge1:" << inner_hoge1.v << std::endl;
return input; std::cout << "f() inner hoge2:" << inner_hoge2.v << std::endl;
} std::cout << "f() inner hoge3:" << inner_hoge3.v << std::endl;
return input;
int main(int argc, char* argv[]) { }
// can only use ‘init_priority’ attribute on file-scope definitions of objects of class type
int main(int argc, char* argv[]) {
// static thread_local Hoge inner_hoge3(++v); // 1 // can only use ‘init_priority’ attribute on file-scope definitions of objects of class type
// static thread_local Hoge inner_hoge2(++v); // 2
// static thread_local Hoge inner_hoge1(++v); // 3 // static thread_local Hoge inner_hoge3(++v); // 1
// static thread_local Hoge inner_hoge2(++v); // 2
// static thread_local Hoge inner_hoge1(++v); // 1 // static thread_local Hoge inner_hoge1(++v); // 3
// static thread_local Hoge inner_hoge2(++v); // 2
// static thread_local Hoge inner_hoge3(++v); // 3 // static thread_local Hoge inner_hoge1(++v); // 1
// static thread_local Hoge inner_hoge2(++v); // 2
// static Hoge inner_hoge3(++v); // 1 // static thread_local Hoge inner_hoge3(++v); // 3
// static Hoge inner_hoge2(++v); // 2
// static Hoge inner_hoge1(++v); // 3 // static Hoge inner_hoge3(++v); // 1
// static Hoge inner_hoge2(++v); // 2
static Hoge inner_hoge1(++v); // 1 // static Hoge inner_hoge1(++v); // 3
static Hoge inner_hoge2(++v); // 2
static Hoge inner_hoge3(++v); // 3 static Hoge inner_hoge1(++v); // 1
static Hoge inner_hoge2(++v); // 2
// Hoge inner_hoge1(++v); // 1 static Hoge inner_hoge3(++v); // 3
// Hoge inner_hoge2(++v); // 2
// Hoge inner_hoge3(++v); // 3 // Hoge inner_hoge1(++v); // 1
// Hoge inner_hoge2(++v); // 2
std::cout << "outer hoge1:" << outer_hoge1.v << std::endl; // Hoge inner_hoge3(++v); // 3
std::cout << "outer hoge2:" << outer_hoge2.v << std::endl;
std::cout << "outer hoge3:" << outer_hoge3.v << std::endl; std::cout << "outer hoge1:" << outer_hoge1.v << std::endl;
std::cout << "outer hoge2:" << outer_hoge2.v << std::endl;
std::cout << "inner hoge1:" << inner_hoge1.v << std::endl; std::cout << "outer hoge3:" << outer_hoge3.v << std::endl;
std::cout << "inner hoge2:" << inner_hoge2.v << std::endl;
std::cout << "inner hoge3:" << inner_hoge3.v << std::endl; std::cout << "inner hoge1:" << inner_hoge1.v << std::endl;
return 0; std::cout << "inner hoge2:" << inner_hoge2.v << std::endl;
} std::cout << "inner hoge3:" << inner_hoge3.v << std::endl;
``` return 0;
}
#### 確実にグローバル変数の値を取得するためには、関数スコープのグローバル変数を利用すると良い ```
``` cpp
#include <sys/types.h> #### 確実にグローバル変数の値を取得するためには、関数スコープのグローバル変数を利用すると良い
#include <unistd.h> ``` cpp
#include <sys/types.h>
#include <iostream> #include <unistd.h>
#include <string>
#include <iostream>
pid_t get_cached_pid() { #include <string>
static pid_t pid = []() { return getpid(); }();
return pid; pid_t get_cached_pid() {
} static pid_t pid = []() { return getpid(); }();
void print_pid(std::string message); return pid;
void print_cached_pid(std::string message); }
void print_pid(std::string message);
struct PrePID { void print_cached_pid(std::string message);
PrePID() {
print_pid("pre pid global variable constructor"); struct PrePID {
print_cached_pid("pre pid global variable constructor"); PrePID() {
} print_pid("pre pid global variable constructor");
} pre_pid; print_cached_pid("pre pid global variable constructor");
}
pid_t pid = getpid(); } pre_pid;
struct PostPID { pid_t pid = getpid();
PostPID() {
print_pid("post pid global variable constructor"); struct PostPID {
print_cached_pid("pre pid global variable constructor"); PostPID() {
} print_pid("post pid global variable constructor");
} post_pid; print_cached_pid("pre pid global variable constructor");
}
void print_pid(std::string message) { } post_pid;
printf("pid at %s:%d\n", message.c_str(), pid);
} void print_pid(std::string message) {
void print_cached_pid(std::string message) { printf("pid at %s:%d\n", message.c_str(), pid);
printf("cached pid at %s:%d\n", message.c_str(), get_cached_pid()); }
} void print_cached_pid(std::string message) {
printf("cached pid at %s:%d\n", message.c_str(), get_cached_pid());
int main(int argc, char* argv[]) { }
print_pid("main func");
return 0; int main(int argc, char* argv[]) {
} print_pid("main func");
``` return 0;
}
``` bash ```
pid at pre pid global variable constructor:0
cached pid at pre pid global variable constructor:96839 ``` bash
pid at post pid global variable constructor:96839 pid at pre pid global variable constructor:0
cached pid at pre pid global variable constructor:96839 cached pid at pre pid global variable constructor:96839
pid at main func:96839 pid at post pid global variable constructor:96839
``` cached pid at pre pid global variable constructor:96839
pid at main func:96839
## `nullptr`のクラスオブジェクトでメソッドを呼び出してみる ```
:warning: クラスオブジェクトのポインタを参照する処理(e.g. フィールドを参照する)などが内部で行わなければ問題なく実行できてしまう ## `nullptr`のクラスオブジェクトでメソッドを呼び出してみる
``` cpp :warning: クラスオブジェクトのポインタを参照する処理(e.g. フィールドを参照する)などが内部で行わなければ問題なく実行できてしまう
#include <iostream>
``` cpp
class Hoge { #include <iostream>
public:
void PrintArgValue(int x) { std::cout << "arg x=" << x << std::endl; } class Hoge {
void PrintFieldValue() { std::cout << "field x=" << x_ << std::endl; } public:
void PrintArgValue(int x) { std::cout << "arg x=" << x << std::endl; }
private: void PrintFieldValue() { std::cout << "field x=" << x_ << std::endl; }
int x_ = 123;
}; private:
int x_ = 123;
int main(int argc, char* argv[]) { };
Hoge hoge;
hoge.PrintArgValue(456); int main(int argc, char* argv[]) {
hoge.PrintFieldValue(); Hoge hoge;
hoge.PrintArgValue(456);
Hoge* hoge_nunllptr = nullptr; hoge.PrintFieldValue();
hoge_nunllptr->PrintArgValue(789);
(reinterpret_cast<Hoge*>(0))->PrintArgValue(789); Hoge* hoge_nunllptr = nullptr;
// WARN: segmentation fault hoge_nunllptr->PrintArgValue(789);
hoge_nunllptr->PrintFieldValue(); (reinterpret_cast<Hoge*>(0))->PrintArgValue(789);
return 0; // WARN: segmentation fault
} hoge_nunllptr->PrintFieldValue();
``` return 0;
}
## 早見表 ```
* [IEEE 754 演算のルール \- C\+\+ の歩き方 \| CppMap]( https://cppmap.github.io/articles/ieee754-arithmetic/ )
* [C\+\+ 型特性 早見表 \- C\+\+ の歩き方 \| CppMap]( https://cppmap.github.io/articles/type-traits/ ) ## 早見表
* [IEEE 754 演算のルール \- C\+\+ の歩き方 \| CppMap]( https://cppmap.github.io/articles/ieee754-arithmetic/ )
## ADL(Argument Dependent Lookup: 実引数依存の名前探索) * [C\+\+ 型特性 早見表 \- C\+\+ の歩き方 \| CppMap]( https://cppmap.github.io/articles/type-traits/ )
* [C\+\+ ADLとは|問題と危険性、回避方法(明示的回避と事前回避) \| MaryCore]( https://marycore.jp/prog/cpp/about-adl-avoid-adl/ )
* [Argument Dependent Lookup \| 闇夜のC\+\+]( http://cpp.aquariuscode.com/argument-dependent-lookup ) ## ADL(Argument Dependent Lookup: 実引数依存の名前探索)
* [C\+\+ ADLとは|問題と危険性、回避方法(明示的回避と事前回避) \| MaryCore]( https://marycore.jp/prog/cpp/about-adl-avoid-adl/ )
## unnamed namespace in header file * [Argument Dependent Lookup \| 闇夜のC\+\+]( http://cpp.aquariuscode.com/argument-dependent-lookup )
* `.cpp`でnamespaceを定義する場合は1つであるが、ヘッダファイルの場合は複数の翻訳単位で存在することになることに注意(headerで読み込んだファイルは同一ファイル内であるとみなされるため)
* 慣例として、`detail`を利用する(`impl`, `internal`を利用する例もある) ## unnamed namespace in header file
* `detail`: [tinyformat/tinyformat\.h at master · c42f/tinyformat]( https://github.com/c42f/tinyformat/blob/master/tinyformat.h#L181 ) * `.cpp`でnamespaceを定義する場合は1つであるが、ヘッダファイルの場合は複数の翻訳単位で存在することになることに注意(headerで読み込んだファイルは同一ファイル内であるとみなされるため)
* `detail`, `impl`: boost header * 慣例として、`detail`を利用する(`impl`, `internal`を利用する例もある)
* 標準ライブラリは`std::__detail::`を利用している * `detail`: [tinyformat/tinyformat\.h at master · c42f/tinyformat]( https://github.com/c42f/tinyformat/blob/master/tinyformat.h#L181 )
* `detail`, `impl`: boost header
## 文字列 * 標準ライブラリは`std::__detail::`を利用している
### `std::string(nullptr)`は違反
[c\+\+ \- Assign a nullptr to a std::string is safe? \- Stack Overflow]( https://stackoverflow.com/questions/10771864/assign-a-nullptr-to-a-stdstring-is-safe ) ## 文字列
### `std::string(nullptr)`は違反
### null文字の扱い [c\+\+ \- Assign a nullptr to a std::string is safe? \- Stack Overflow]( https://stackoverflow.com/questions/10771864/assign-a-nullptr-to-a-stdstring-is-safe )
* [serial port \- does read\(\) in c read null character \- Stack Overflow]( https://stackoverflow.com/questions/50625105/does-read-in-c-read-null-character )
* `read()``\0`関係なく一定バイト数読む ### null文字の扱い
* [std::string の途中に null 文字が含まれている場合の処理 \- Secret Garden\(Instrumental\)]( http://secret-garden.hatenablog.com/entry/2016/04/14/004729 ) * [serial port \- does read\(\) in c read null character \- Stack Overflow]( https://stackoverflow.com/questions/50625105/does-read-in-c-read-null-character )
* 問題なく表示されるが,`char*`として表示すると区切れる * `read()``\0`関係なく一定バイト数読む
* [c\+\+ \- Copy string data with NULL character inside string to char array \- Stack Overflow]( https://stackoverflow.com/questions/22907430/copy-string-data-with-null-character-inside-string-to-char-array ) * [std::string の途中に null 文字が含まれている場合の処理 \- Secret Garden\(Instrumental\)]( http://secret-garden.hatenablog.com/entry/2016/04/14/004729 )
* 問題なく表示されるが,`char*`として表示すると区切れる
## bit * [c\+\+ \- Copy string data with NULL character inside string to char array \- Stack Overflow]( https://stackoverflow.com/questions/22907430/copy-string-data-with-null-character-inside-string-to-char-array )
### 10bit単位の数値
## bit
:warning: ビットフィールドの以下の性質は処理系定義であるので,移植性を考えると使うべきではないと思われる ### 10bit単位の数値
[variables \- 10 or 12 bit field data type in C\+\+ \- Stack Overflow]( https://stackoverflow.com/questions/29529979/10-or-12-bit-field-data-type-in-c ) :warning: ビットフィールドの以下の性質は処理系定義であるので,移植性を考えると使うべきではないと思われる
``` cpp [variables \- 10 or 12 bit field data type in C\+\+ \- Stack Overflow]( https://stackoverflow.com/questions/29529979/10-or-12-bit-field-data-type-in-c )
struct uint10_t {
uint16_t value : 10; ``` cpp
}; struct uint10_t {
``` uint16_t value : 10;
};
これはどのような仕様? => [ビットフィールド \- cppreference\.com]( https://ja.cppreference.com/w/cpp/language/bit_field ) ```
`10bit`で表現できない数値を代入しようとすると警告が出る これはどのような仕様? => [ビットフィールド \- cppreference\.com]( https://ja.cppreference.com/w/cpp/language/bit_field )
```
xxx.cpp:22:11: warning: implicit truncation from 'int' to bit-field changes value from 2040 to `10bit`で表現できない数値を代入しようとすると警告が出る
1016 [-Wbitfield-constant-conversion] ```
x.value = 0xFF << 3; xxx.cpp:22:11: warning: implicit truncation from 'int' to bit-field changes value from 2040 to
^ ~~~~~~~~~ 1016 [-Wbitfield-constant-conversion]
1 warning generated. x.value = 0xFF << 3;
``` ^ ~~~~~~~~~
1 warning generated.
### 10bit単位のデータをバイナリにパックしたい ```
[c\+\+ \- Efficiently packing 10\-bit data on unaligned byte boundries \- Stack Overflow]( https://stackoverflow.com/questions/34775546/efficiently-packing-10-bit-data-on-unaligned-byte-boundries )? ### 10bit単位のデータをバイナリにパックしたい
`64bit`中の下位`40bit`を利用して`10bit`x4個と`8bit`x5個として,まとめて計算して利用しようとしても,バイト単位で演算されて離れた箇所にデータが散らばり意図通りにならない(さらに,endianの問題も発生する)ため,結果として,単純にバイトごとに無理やり演算する方法が安全となり得る [c\+\+ \- Efficiently packing 10\-bit data on unaligned byte boundries \- Stack Overflow]( https://stackoverflow.com/questions/34775546/efficiently-packing-10-bit-data-on-unaligned-byte-boundries )?
下記のコードだが,シフト演算のみを行っているため,endianは関係ない気がしてきた `64bit`中の下位`40bit`を利用して`10bit`x4個と`8bit`x5個として,まとめて計算して利用しようとしても,バイト単位で演算されて離れた箇所にデータが散らばり意図通りにならない(さらに,endianの問題も発生する)ため,結果として,単純にバイトごとに無理やり演算する方法が安全となり得る
``` cpp
#include <bitset> 下記のコードだが,シフト演算のみを行っているため,endianは関係ない気がしてきた
#include <cstdint> ``` cpp
#include <fstream> #include <bitset>
#include <iostream> #include <cstdint>
#include <vector> #include <fstream>
#include <iostream>
/** #include <vector>
* @brief Pack 10bit data to 8bit array
* /**
* @param[in] upper_data 10bit data(2~9bit data) head pointer. * @brief Pack 10bit data to 8bit array
* @param[in] lower_data 10bit data(0,1bit data) head pointer. *
* @param[in] data_size 10bit data array size * @param[in] upper_data 10bit data(2~9bit data) head pointer.
* @param[out] buffer 8bit array buffer head pointer(destination) required: 0 filled * @param[in] lower_data 10bit data(0,1bit data) head pointer.
*/ * @param[in] data_size 10bit data array size
void Pack10bitPerByte(const uint8_t* upper_data, const uint8_t* lower_data, * @param[out] buffer 8bit array buffer head pointer(destination) required: 0 filled
const size_t data_size, uint8_t* buffer) { */
int bit_offset = 0; void Pack10bitPerByte(const uint8_t* upper_data, const uint8_t* lower_data,
for (int i = 0; i < data_size; i++) { const size_t data_size, uint8_t* buffer) {
uint32_t upper_offset = bit_offset % 8; int bit_offset = 0;
uint32_t lower_offset = (bit_offset + 2) % 8; for (int i = 0; i < data_size; i++) {
uint32_t upper_value = static_cast<uint32_t>(upper_data[i]); uint32_t upper_offset = bit_offset % 8;
uint32_t lower_value = static_cast<uint32_t>(lower_data[i]) & 0x03; uint32_t lower_offset = (bit_offset + 2) % 8;
uint32_t upper_value = static_cast<uint32_t>(upper_data[i]);
int byte_offset = bit_offset / 8; uint32_t lower_value = static_cast<uint32_t>(lower_data[i]) & 0x03;
// NOTE: set per byte (align is 2bit) int byte_offset = bit_offset / 8;
// NOTE: upper bit of upper data
buffer[byte_offset] |= static_cast<uint8_t>((upper_value >> upper_offset)); // NOTE: set per byte (align is 2bit)
// NOTE: lower bit of upper data // NOTE: upper bit of upper data
if (upper_offset != 0) { buffer[byte_offset] |= static_cast<uint8_t>((upper_value >> upper_offset));
buffer[byte_offset + 1] |= // NOTE: lower bit of upper data
static_cast<uint8_t>((upper_value << ((8 - upper_offset) % 8))); if (upper_offset != 0) {
} buffer[byte_offset + 1] |=
buffer[byte_offset + 1] |= static_cast<uint8_t>((upper_value << ((8 - upper_offset) % 8)));
static_cast<uint8_t>(lower_value << ((8 - lower_offset) % 8)); }
bit_offset += 10; buffer[byte_offset + 1] |=
} static_cast<uint8_t>(lower_value << ((8 - lower_offset) % 8));
} bit_offset += 10;
}
/** }
* @brief Pack 10bit data to 8bit array (only for little endian)
* /**
* @param[in] upper_data 10bit data(2~9bit data) head pointer. * @brief Pack 10bit data to 8bit array (only for little endian)
* @param[in] lower_data 10bit data(0,1bit data) head pointer. *
* @param[in] data_size 10bit data array size * @param[in] upper_data 10bit data(2~9bit data) head pointer.
* @param[out] buffer 8bit array buffer head pointer(destination) * @param[in] lower_data 10bit data(0,1bit data) head pointer.
*/ * @param[in] data_size 10bit data array size
void Pack10bitPer40Bit(const uint8_t* upper_data, const uint8_t* lower_data, * @param[out] buffer 8bit array buffer head pointer(destination)
const size_t data_size, uint8_t* buffer) { */
int dst_offset = 0; void Pack10bitPer40Bit(const uint8_t* upper_data, const uint8_t* lower_data,
for (int i = 0; i < data_size;) { const size_t data_size, uint8_t* buffer) {
int bit_offset = 0; int dst_offset = 0;
uint64_t tmp_calc_space = 0; for (int i = 0; i < data_size;) {
int pack_calc_max_n = (40 / 10); int bit_offset = 0;
pack_calc_max_n = uint64_t tmp_calc_space = 0;
pack_calc_max_n < (data_size - i) ? pack_calc_max_n : (data_size - i); int pack_calc_max_n = (40 / 10);
for (int j = 0; j < pack_calc_max_n; i++, j++) { pack_calc_max_n =
uint64_t input_value = (static_cast<uint64_t>(upper_data[i]) << 2) | pack_calc_max_n < (data_size - i) ? pack_calc_max_n : (data_size - i);
(static_cast<uint64_t>(lower_data[i] & 0x03)); for (int j = 0; j < pack_calc_max_n; i++, j++) {
// NOTE: big endian uint64_t input_value = (static_cast<uint64_t>(upper_data[i]) << 2) |
// int dst_bit_offset = bit_offset; (static_cast<uint64_t>(lower_data[i] & 0x03));
// NOTE: little endian // NOTE: big endian
int dst_bit_offset = (40 - 10) - bit_offset; // int dst_bit_offset = bit_offset;
tmp_calc_space |= input_value << dst_bit_offset; // NOTE: little endian
bit_offset += 10; int dst_bit_offset = (40 - 10) - bit_offset;
} tmp_calc_space |= input_value << dst_bit_offset;
// NOTE: for big endian bit_offset += 10;
// for (int j = 0; j < bit_offset; j += 8) { }
// NOTE: for little endian // NOTE: for big endian
for (int j = (bit_offset + (8 - 1)) / 8 * 8 - 8; j >= 0; j -= 8) { // for (int j = 0; j < bit_offset; j += 8) {
buffer[dst_offset] = static_cast<uint8_t>((tmp_calc_space >> j)); // NOTE: for little endian
dst_offset++; for (int j = (bit_offset + (8 - 1)) / 8 * 8 - 8; j >= 0; j -= 8) {
} buffer[dst_offset] = static_cast<uint8_t>((tmp_calc_space >> j));
} dst_offset++;
} }
}
int main(int argc, char* argv[]) { }
std::vector<uint8_t> upper_data = {0x0F, 0x0F, 0x0F, 0x0F};
std::vector<uint8_t> lower_data = {0, 1, 2, 3}; int main(int argc, char* argv[]) {
std::vector<uint8_t> data(5); std::vector<uint8_t> upper_data = {0x0F, 0x0F, 0x0F, 0x0F};
std::vector<uint8_t> expected_data = {0x0f, 0x03, 0xd0, 0xf8, 0x3f}; std::vector<uint8_t> lower_data = {0, 1, 2, 3};
int bit_offset = 0; std::vector<uint8_t> data(5);
int byte_offset = 0; std::vector<uint8_t> expected_data = {0x0f, 0x03, 0xd0, 0xf8, 0x3f};
int size = 4; int bit_offset = 0;
int byte_offset = 0;
std::cout << "Pack10bitPerByte" << std::endl; int size = 4;
Pack10bitPerByte(upper_data.data(), lower_data.data(), size, data.data());
for (auto&& v : data) { std::cout << "Pack10bitPerByte" << std::endl;
std::cout << (int)v << std::endl; Pack10bitPerByte(upper_data.data(), lower_data.data(), size, data.data());
} for (auto&& v : data) {
std::cout << (int)v << std::endl;
// NOTE: test }
for (int i = 0; i < expected_data.size(); i++) {
if (data[i] != expected_data[i]) { // NOTE: test
std::cout << "[" << i << "] data:" << (int)data[i] for (int i = 0; i < expected_data.size(); i++) {
<< "!= expected_data:" << (int)expected_data[i] << std::endl; if (data[i] != expected_data[i]) {
} std::cout << "[" << i << "] data:" << (int)data[i]
} << "!= expected_data:" << (int)expected_data[i] << std::endl;
std::fill(data.begin(), data.end(), 0); }
}
std::cout << "Pack10bitPerByte" << std::endl; std::fill(data.begin(), data.end(), 0);
Pack10bitPer40Bit(upper_data.data(), lower_data.data(), size, data.data());
std::cout << "Pack10bitPerByte" << std::endl;
for (auto&& v : data) { Pack10bitPer40Bit(upper_data.data(), lower_data.data(), size, data.data());
std::cout << (int)v << std::endl;
} for (auto&& v : data) {
std::cout << (int)v << std::endl;
// NOTE: test }
for (int i = 0; i < expected_data.size(); i++) {
if (data[i] != expected_data[i]) { // NOTE: test
std::cout << "[" << i << "] data:" << (int)data[i] for (int i = 0; i < expected_data.size(); i++) {
<< "!= expected_data:" << (int)expected_data[i] << std::endl; if (data[i] != expected_data[i]) {
} std::cout << "[" << i << "] data:" << (int)data[i]
} << "!= expected_data:" << (int)expected_data[i] << std::endl;
}
std::ofstream dst; }
dst.open("output.raw", std::ios::out | std::ios::binary | std::ios::trunc);
if (!dst) { std::ofstream dst;
std::cerr << "failed to write file: " << std::endl; dst.open("output.raw", std::ios::out | std::ios::binary | std::ios::trunc);
return 1; if (!dst) {
} std::cerr << "failed to write file: " << std::endl;
dst.write((const char*)data.data(), data.size()); return 1;
return 0; }
} dst.write((const char*)data.data(), data.size());
``` return 0;
}
ちなみに,`Doxygen`コメントの`in`,`out`は概念上の考え方に近い?(例えば,`&`での参照渡しを考慮したときに,それ自身の値が書き換わる場合と,そのポインタが指し示す値が書き換わる場合がある) ```
[documentation \- Is that an in or in/out parameter? Doxygen, C\+\+ \- Stack Overflow]( https://stackoverflow.com/questions/47732665/is-that-an-in-or-in-out-parameter-doxygen-c ) ちなみに,`Doxygen`コメントの`in`,`out`は概念上の考え方に近い?(例えば,`&`での参照渡しを考慮したときに,それ自身の値が書き換わる場合と,そのポインタが指し示す値が書き換わる場合がある)
## シフト演算 [documentation \- Is that an in or in/out parameter? Doxygen, C\+\+ \- Stack Overflow]( https://stackoverflow.com/questions/47732665/is-that-an-in-or-in-out-parameter-doxygen-c )
* :warning: 意図せずにオーバーフローを引き起こす可能性がある(特に,定数値に対して処理を行う場合 `0xFF << x`とする際に,`uint32_t`の範囲を超えてしまう可能性があり得る)
* :warning: [INT34\-C\. 負のビット数のシフトやオペランドのビット数以上のシフトを行わない]( https://www.jpcert.or.jp/sc-rules/c-int34-c.html ) ## シフト演算
* 特に,__ビット数以上のシフト__が未定義であることに注意(ビット数以上のシフトを行うことで,`0`となることを意図するような設計に注意) * :warning: 意図せずにオーバーフローを引き起こす可能性がある(特に,定数値に対して処理を行う場合 `0xFF << x`とする際に,`uint32_t`の範囲を超えてしまう可能性があり得る)
* :warning: [INT34\-C\. 負のビット数のシフトやオペランドのビット数以上のシフトを行わない]( https://www.jpcert.or.jp/sc-rules/c-int34-c.html )
``` cpp * 特に,__ビット数以上のシフト__が未定義であることに注意(ビット数以上のシフトを行うことで,`0`となることを意図するような設計に注意)
#include <cassert>
``` cpp
int main(int argc, char* argv[]) { #include <cassert>
// NOTE: NG case: overflow(32bit << any bit)
assert(0x000000000 == (0xF0000000 << 4)); int main(int argc, char* argv[]) {
assert(0x000000000 == (0xF0000000 << 4UL)); // NOTE: NG case: overflow(32bit << any bit)
assert(0x000000000 == (0xF0000000 << static_cast<int8_t>(4))); assert(0x000000000 == (0xF0000000 << 4));
assert(0x000000000 == (0xF0000000 << static_cast<uint8_t>(4))); assert(0x000000000 == (0xF0000000 << 4UL));
assert(0x000000000 == (0xF0000000 << static_cast<int16_t>(4))); assert(0x000000000 == (0xF0000000 << static_cast<int8_t>(4)));
assert(0x000000000 == (0xF0000000 << static_cast<uint16_t>(4))); assert(0x000000000 == (0xF0000000 << static_cast<uint8_t>(4)));
assert(0x000000000 == (0xF0000000 << static_cast<int32_t>(4))); assert(0x000000000 == (0xF0000000 << static_cast<int16_t>(4)));
assert(0x000000000 == (0xF0000000 << static_cast<uint32_t>(4))); assert(0x000000000 == (0xF0000000 << static_cast<uint16_t>(4)));
assert(0x000000000 == (0xF0000000 << static_cast<int64_t>(4))); assert(0x000000000 == (0xF0000000 << static_cast<int32_t>(4)));
assert(0x000000000 == (0xF0000000 << static_cast<uint64_t>(4))); assert(0x000000000 == (0xF0000000 << static_cast<uint32_t>(4)));
// NOTE: OK case: 64bit << any bit assert(0x000000000 == (0xF0000000 << static_cast<int64_t>(4)));
assert(0xF00000000 == (0xF0000000UL << 4)); assert(0x000000000 == (0xF0000000 << static_cast<uint64_t>(4)));
assert(0xF00000000 == (0xF0000000UL << 4UL)); // NOTE: OK case: 64bit << any bit
assert(0xF00000000 == (0xF0000000UL << static_cast<int8_t>(4))); assert(0xF00000000 == (0xF0000000UL << 4));
assert(0xF00000000 == (0xF0000000UL << static_cast<uint8_t>(4))); assert(0xF00000000 == (0xF0000000UL << 4UL));
assert(0xF00000000 == (0xF0000000UL << static_cast<int16_t>(4))); assert(0xF00000000 == (0xF0000000UL << static_cast<int8_t>(4)));
assert(0xF00000000 == (0xF0000000UL << static_cast<uint16_t>(4))); assert(0xF00000000 == (0xF0000000UL << static_cast<uint8_t>(4)));
assert(0xF00000000 == (0xF0000000UL << static_cast<int32_t>(4))); assert(0xF00000000 == (0xF0000000UL << static_cast<int16_t>(4)));
assert(0xF00000000 == (0xF0000000UL << static_cast<uint32_t>(4))); assert(0xF00000000 == (0xF0000000UL << static_cast<uint16_t>(4)));
assert(0xF00000000 == (0xF0000000UL << static_cast<int64_t>(4))); assert(0xF00000000 == (0xF0000000UL << static_cast<int32_t>(4)));
assert(0xF00000000 == (0xF0000000UL << static_cast<uint64_t>(4))); assert(0xF00000000 == (0xF0000000UL << static_cast<uint32_t>(4)));
return 0; assert(0xF00000000 == (0xF0000000UL << static_cast<int64_t>(4)));
} assert(0xF00000000 == (0xF0000000UL << static_cast<uint64_t>(4)));
``` return 0;
}
## 64bit/32bitマシン別の型サイズ例 ```
``` cpp ## 64bit/32bitマシン別の型サイズ例
#include <cinttypes>
#include <iostream> ``` cpp
#include <string> #include <cinttypes>
#include <iostream>
#define SIZEOF_PRINT(x) \ #include <string>
std::cout << "" #x << ":" << sizeof(x) << "B" << std::endl;
int main(int argc, char* argv[]) { #define SIZEOF_PRINT(x) \
SIZEOF_PRINT(int); std::cout << "" #x << ":" << sizeof(x) << "B" << std::endl;
SIZEOF_PRINT(long); int main(int argc, char* argv[]) {
SIZEOF_PRINT(unsigned long); SIZEOF_PRINT(int);
SIZEOF_PRINT(unsigned long long); SIZEOF_PRINT(long);
SIZEOF_PRINT(uint64_t); SIZEOF_PRINT(unsigned long);
return 0; SIZEOF_PRINT(unsigned long long);
} SIZEOF_PRINT(uint64_t);
``` return 0;
}
``` ```
# 64bitマシン
int:4B ```
long:8B # 64bitマシン
unsigned long:8B int:4B
unsigned long long:8B long:8B
uint64_t:8B unsigned long:8B
unsigned long long:8B
# 32bitマシン uint64_t:8B
int:4B
long:4B # 32bitマシン
unsigned long:4B int:4B
unsigned long long:8B long:4B
uint64_t:8B unsigned long:4B
``` unsigned long long:8B
uint64_t:8B
## hash ```
intのhashのinputとoutputが一致してしまう(余計な演算なしにhashの定義を満たす)ため、`std::to_string()`をかますと意図する結果が得られる ## hash
* [c++ - Why std::hash\<int> seems to be identity function - Stack Overflow]( https://stackoverflow.com/questions/38304877/why-stdhashint-seems-to-be-identity-function ) intのhashのinputとoutputが一致してしまう(余計な演算なしにhashの定義を満たす)ため、`std::to_string()`をかますと意図する結果が得られる
* [c++ - hash value of int is the same number - Stack Overflow]( https://stackoverflow.com/questions/19734875/hash-value-of-int-is-the-same-number )
* [c++ - Why std::hash\<int> seems to be identity function - Stack Overflow]( https://stackoverflow.com/questions/38304877/why-stdhashint-seems-to-be-identity-function )
* [c++ - hash value of int is the same number - Stack Overflow]( https://stackoverflow.com/questions/19734875/hash-value-of-int-is-the-same-number )
``` cpp
#include <functional> // for std::hash
#include <iostream> ``` cpp
#include <string> #include <functional> // for std::hash
#include <iostream>
int main() { #include <string>
std::hash<std::string> string_hash;
for (int i = 0; i < 10; i++) { int main() {
std::cout << "" << i << ":" << string_hash(std::to_string(i)) << std::endl; std::hash<std::string> string_hash;
} for (int i = 0; i < 10; i++) {
std::hash<int> int_hash; std::cout << "" << i << ":" << string_hash(std::to_string(i)) << std::endl;
for (int i = 0; i < 10; i++) { }
std::cout << "" << i << ":" << int_hash(i) << std::endl; std::hash<int> int_hash;
} for (int i = 0; i < 10; i++) {
} std::cout << "" << i << ":" << int_hash(i) << std::endl;
``` }
}
``` ```
0:10408321403207385874
1:11413460447292444913 ```
2:17472595041006102391 0:10408321403207385874
3:11275350073939794026 1:11413460447292444913
4:2169371982377735806 2:17472595041006102391
5:16141698810441253349 3:11275350073939794026
6:10103374131519304989 4:2169371982377735806
7:5566429635965498611 5:16141698810441253349
8:14908220028612439606 6:10103374131519304989
9:8705150437960110905 7:5566429635965498611
0:0 8:14908220028612439606
1:1 9:8705150437960110905
2:2 0:0
3:3 1:1
4:4 2:2
5:5 3:3
6:6 4:4
7:7 5:5
8:8 6:6
9:9 7:7
``` 8:8
9:9
## [c++11 - golang-style "defer" in C++ - Stack Overflow]( https://stackoverflow.com/questions/33050620/golang-style-defer-in-c ) ```
``` cpp ## [c++11 - golang-style "defer" in C++ - Stack Overflow]( https://stackoverflow.com/questions/33050620/golang-style-defer-in-c )
#include <iostream>
#include <memory> ``` cpp
#include <iostream>
using defer = std::shared_ptr<void>; #include <memory>
int main() { using defer = std::shared_ptr<void>;
std::cout << "start" << std::endl;
defer _(nullptr, [](...) { std::cout << "Hello, World!" << std::endl; }); int main() {
// or std::cout << "start" << std::endl;
// std::shared_ptr<void> defer(nullptr, [](...) { std::cout << "Hello, World!" << std::endl; }); defer _(nullptr, [](...) { std::cout << "Hello, World!" << std::endl; });
// or // or
// std::shared_ptr<void> defer(nullptr, [](void*) { std::cout << "Hello, World!" << std::endl; }); // std::shared_ptr<void> defer(nullptr, [](...) { std::cout << "Hello, World!" << std::endl; });
// or // or
// std::unique_ptr<void, std::function<void(void*)>> defer(nullptr, [](void*) { std::cout << "Hello, World!" << std::endl; }); // std::shared_ptr<void> defer(nullptr, [](void*) { std::cout << "Hello, World!" << std::endl; });
std::cout << "end" << std::endl; // or
return 0; // std::unique_ptr<void, std::function<void(void*)>> defer(nullptr, [](void*) { std::cout << "Hello, World!" << std::endl; });
} std::cout << "end" << std::endl;
``` return 0;
}
`...`は可変長引数であるが、直接`void*`を指定してもよい ```
[Variadic functions \- cppreference\.com]( https://en.cppreference.com/w/cpp/utility/variadic ) `...`は可変長引数であるが、直接`void*`を指定してもよい
`std::unique_ptr`を利用する場合には、明示的に関数の型を記述する必要がある [Variadic functions \- cppreference\.com]( https://en.cppreference.com/w/cpp/utility/variadic )
## C `std::unique_ptr`を利用する場合には、明示的に関数の型を記述する必要がある
### `C`では,関数宣言にて`foo()`ではなく,`foo(void)`とする(`C++`では特に問題はない)
* [【C言語】引数なしの関数には void を書いた方がよいという話 \- 0x19f \(Shinya Kato\) の日報]( https://0x19f.hatenablog.com/entry/2019/04/17/213231 ) ## C
* [DCL20\-C\. 引数を受け付けない関数の場合も必ず void を指定する]( https://www.jpcert.or.jp/sc-rules/c-dcl20-c.html ) ### `C`では,関数宣言にて`foo()`ではなく,`foo(void)`とする(`C++`では特に問題はない)
* [【C言語】引数なしの関数には void を書いた方がよいという話 \- 0x19f \(Shinya Kato\) の日報]( https://0x19f.hatenablog.com/entry/2019/04/17/213231 )
### [Cのプログラムでたまに出てくる関数内の無意味なvoid宣言の意味 \- $ cat /var/log/shin]( https://shin.hateblo.jp/entry/2013/03/13/175059 ) * [DCL20\-C\. 引数を受け付けない関数の場合も必ず void を指定する]( https://www.jpcert.or.jp/sc-rules/c-dcl20-c.html )
## 実現したいことからの逆引き ### [Cのプログラムでたまに出てくる関数内の無意味なvoid宣言の意味 \- $ cat /var/log/shin]( https://shin.hateblo.jp/entry/2013/03/13/175059 )
### 外部コマンドを安全に実行したい
[cpp\-examples/examples/fork\-exec at master · umaumax/cpp\-examples]( https://github.com/umaumax/cpp-examples/tree/master/examples/fork-exec ) ## 実現したいことからの逆引き
### 外部コマンドを安全に実行したい
### c++のコードの概要を知りたい [cpp\-examples/examples/fork\-exec at master · umaumax/cpp\-examples]( https://github.com/umaumax/cpp-examples/tree/master/examples/fork-exec )
[Doxygen · Wiki · umaumax / memo · GitLab]( https://gitlab.com/umaumax/memo/-/wikis/Doxygen )
### c++のコードの概要を知りたい
### vectorやmapなどのコレクションをダンプしたい [Doxygen · Wiki · umaumax / memo · GitLab]( https://gitlab.com/umaumax/memo/-/wikis/Doxygen )
e.g. [neostream.hpp]( https://gist.github.com/umaumax/9765462a4a7e85dcb52515b7921191fd )
### vectorやmapなどのコレクションをダンプしたい
### 標準入出力先が端末かどうかを調べたい e.g. [neostream.hpp]( https://gist.github.com/umaumax/9765462a4a7e85dcb52515b7921191fd )
``` cpp
#include <unistd.h> ### 標準入出力先が端末かどうかを調べたい
``` cpp
#include <iostream> #include <unistd.h>
int main() { #include <iostream>
const bool stdin_console = isatty(STDIN_FILENO);
const bool stdout_console = isatty(STDOUT_FILENO); int main() {
const bool stderr_console = isatty(STDERR_FILENO); const bool stdin_console = isatty(STDIN_FILENO);
const bool stdout_console = isatty(STDOUT_FILENO);
std::cout << "stdin is" << (stdin_console ? "" : " not") << " terminal" << std::endl; const bool stderr_console = isatty(STDERR_FILENO);
std::cout << "stdout is" << (stdout_console ? "" : " not") << " terminal" << std::endl;
std::cout << "stderr is" << (stderr_console ? "" : " not") << " terminal" << std::endl; std::cout << "stdin is" << (stdin_console ? "" : " not") << " terminal" << std::endl;
} std::cout << "stdout is" << (stdout_console ? "" : " not") << " terminal" << std::endl;
``` std::cout << "stderr is" << (stderr_console ? "" : " not") << " terminal" << std::endl;
}
## 落とし穴(pitfall) ```
### [cos と std::cos は別物だっていう話 \- akihiko’s tech note]( https://aki-yam.hatenablog.com/entry/20080826/1219757584 )
## 落とし穴(pitfall)
* リンクの通り,`::cos(float)``double`となっている ### [cos と std::cos は別物だっていう話 \- akihiko’s tech note]( https://aki-yam.hatenablog.com/entry/20080826/1219757584 )
* `using namespace std;`の有無で挙動が変化するので注意
* `x86``arm`(32bit)で`std::cos(float)`の計算結果が異なる * リンクの通り,`::cos(float)``double`となっている
* `using namespace std;`の有無で挙動が変化するので注意
``` cpp * `x86``arm`(32bit)で`std::cos(float)`の計算結果が異なる
#include <cmath>
#include <iomanip> ``` cpp
#include <iostream> #include <cmath>
#include <limits> #include <iomanip>
#include <string> #include <iostream>
#include <typeinfo> #include <limits>
#include <string>
#define FORMAT(msg, value) \ #include <typeinfo>
msg << (value) << " " << typeid(value).name() << std::endl
#define FORMAT(msg, value) \
inline void test(float f_rad) { msg << (value) << " " << typeid(value).name() << std::endl
double d_rad = static_cast<double>(f_rad);
std::cout << std::fixed inline void test(float f_rad) {
<< std::setprecision(std::numeric_limits<double>::max_digits10 + 1) double d_rad = static_cast<double>(f_rad);
<< "float =" << f_rad << std::endl std::cout << std::fixed
<< "double =" << d_rad << std::endl << std::setprecision(std::numeric_limits<double>::max_digits10 + 1)
<< FORMAT("::cos(float) =", ::cos(f_rad)) << "float =" << f_rad << std::endl
<< FORMAT("::cos(double) =", ::cos(d_rad)) << "double =" << d_rad << std::endl
<< FORMAT("std::cos(float) =", std::cos(f_rad)) << FORMAT("::cos(float) =", ::cos(f_rad))
<< FORMAT("std::cos(double)=", std::cos(d_rad)) << FORMAT("::cos(double) =", ::cos(d_rad))
<< FORMAT("cos(float) =", cos(f_rad)) << FORMAT("std::cos(float) =", std::cos(f_rad))
<< FORMAT("cos(double) =", cos(d_rad)); << FORMAT("std::cos(double)=", std::cos(d_rad))
{ << FORMAT("cos(float) =", cos(f_rad))
using namespace std; << FORMAT("cos(double) =", cos(d_rad));
std::cout << std::fixed {
<< std::setprecision(std::numeric_limits<double>::max_digits10 + 1) using namespace std;
<< "using namespace std;" << std::endl std::cout << std::fixed
<< FORMAT("cos(float) =", cos(f_rad)) << std::setprecision(std::numeric_limits<double>::max_digits10 + 1)
<< FORMAT("cos(double) =", cos(d_rad)); << "using namespace std;" << std::endl
} << FORMAT("cos(float) =", cos(f_rad))
} << FORMAT("cos(double) =", cos(d_rad));
}
int main(int argc, char* argv[]) { }
std::cout << "[test case]: 0.5235987902f" << std::endl;
test(0.5235987902f); int main(int argc, char* argv[]) {
std::cout << std::endl; std::cout << "[test case]: 0.5235987902f" << std::endl;
std::cout << "[test case]: std::stof(\"0.5235987902\")" << std::endl; test(0.5235987902f);
test(std::stof("0.5235987902")); std::cout << std::endl;
return 0; std::cout << "[test case]: std::stof(\"0.5235987902\")" << std::endl;
} test(std::stof("0.5235987902"));
``` return 0;
}
x86 ```
``` log
[test case]: 0.5235987902f x86
float =0.523598790168762207 ``` log
double =0.523598790168762207 [test case]: 0.5235987902f
::cos(float) =0.866025396499206845 d float =0.523598790168762207
::cos(double) =0.866025396499206845 d double =0.523598790168762207
std::cos(float) =0.866025388240814209 f ::cos(float) =0.866025396499206845 d
std::cos(double)=0.866025396499206845 d ::cos(double) =0.866025396499206845 d
cos(float) =0.866025396499206845 d std::cos(float) =0.866025388240814209 f
cos(double) =0.866025396499206845 d std::cos(double)=0.866025396499206845 d
using namespace std; cos(float) =0.866025396499206845 d
cos(float) =0.866025388240814209 f cos(double) =0.866025396499206845 d
cos(double) =0.866025396499206845 d using namespace std;
cos(float) =0.866025388240814209 f
[test case]: std::stof("0.5235987902") cos(double) =0.866025396499206845 d
float =0.523598790168762207
double =0.523598790168762207 [test case]: std::stof("0.5235987902")
::cos(float) =0.866025396499206845 d float =0.523598790168762207
::cos(double) =0.866025396499206845 d double =0.523598790168762207
std::cos(float) =0.866025388240814209 f ::cos(float) =0.866025396499206845 d
std::cos(double)=0.866025396499206845 d ::cos(double) =0.866025396499206845 d
cos(float) =0.866025396499206845 d std::cos(float) =0.866025388240814209 f
cos(double) =0.866025396499206845 d std::cos(double)=0.866025396499206845 d
using namespace std; cos(float) =0.866025396499206845 d
cos(float) =0.866025388240814209 f cos(double) =0.866025396499206845 d
cos(double) =0.866025396499206845 d using namespace std;
``` cos(float) =0.866025388240814209 f
cos(double) =0.866025396499206845 d
arm ```
``` log
[test case]: 0.5235987902f arm
float =0.523598790168762207 ``` log
double =0.523598790168762207 [test case]: 0.5235987902f
::cos(float) =0.866025396499206845 d float =0.523598790168762207
::cos(double) =0.866025396499206845 d double =0.523598790168762207
std::cos(float) =0.866025447845458984 f ::cos(float) =0.866025396499206845 d
std::cos(double)=0.866025396499206845 d ::cos(double) =0.866025396499206845 d
cos(float) =0.866025396499206845 d std::cos(float) =0.866025447845458984 f
cos(double) =0.866025396499206845 d std::cos(double)=0.866025396499206845 d
using namespace std; cos(float) =0.866025396499206845 d
cos(float) =0.866025447845458984 f cos(double) =0.866025396499206845 d
cos(double) =0.866025396499206845 d using namespace std;
cos(float) =0.866025447845458984 f
[test case]: std::stof("0.5235987902") cos(double) =0.866025396499206845 d
float =0.523598790168762207
double =0.523598790168762207 [test case]: std::stof("0.5235987902")
::cos(float) =0.866025396499206845 d float =0.523598790168762207
::cos(double) =0.866025396499206845 d double =0.523598790168762207
std::cos(float) =0.866025447845458984 f ::cos(float) =0.866025396499206845 d
std::cos(double)=0.866025396499206845 d ::cos(double) =0.866025396499206845 d
cos(float) =0.866025396499206845 d std::cos(float) =0.866025447845458984 f
cos(double) =0.866025396499206845 d std::cos(double)=0.866025396499206845 d
using namespace std; cos(float) =0.866025396499206845 d
cos(float) =0.866025447845458984 f cos(double) =0.866025396499206845 d
cos(double) =0.866025396499206845 d using namespace std;
``` cos(float) =0.866025447845458984 f
cos(double) =0.866025396499206845 d
``` diff ```
--- x86.txt 2021-02-01 18:08:12.539836723 +0900
+++ arm.txt 2021-02-01 18:08:15.675867962 +0900 ``` diff
@@ -3,12 +3,12 @@ --- x86.txt 2021-02-01 18:08:12.539836723 +0900
double =0.523598790168762207 +++ arm.txt 2021-02-01 18:08:15.675867962 +0900
::cos(float) =0.866025396499206845 d @@ -3,12 +3,12 @@
::cos(double) =0.866025396499206845 d double =0.523598790168762207
-std::cos(float) =0.866025388240814209 f ::cos(float) =0.866025396499206845 d
+std::cos(float) =0.866025447845458984 f ::cos(double) =0.866025396499206845 d
std::cos(double)=0.866025396499206845 d -std::cos(float) =0.866025388240814209 f
cos(float) =0.866025396499206845 d +std::cos(float) =0.866025447845458984 f
cos(double) =0.866025396499206845 d std::cos(double)=0.866025396499206845 d
using namespace std; cos(float) =0.866025396499206845 d
-cos(float) =0.866025388240814209 f cos(double) =0.866025396499206845 d
+cos(float) =0.866025447845458984 f using namespace std;
cos(double) =0.866025396499206845 d -cos(float) =0.866025388240814209 f
+cos(float) =0.866025447845458984 f
[test case]: std::stof("0.5235987902") cos(double) =0.866025396499206845 d
@@ -16,10 +16,10 @@
double =0.523598790168762207 [test case]: std::stof("0.5235987902")
::cos(float) =0.866025396499206845 d @@ -16,10 +16,10 @@
::cos(double) =0.866025396499206845 d double =0.523598790168762207
-std::cos(float) =0.866025388240814209 f ::cos(float) =0.866025396499206845 d
+std::cos(float) =0.866025447845458984 f ::cos(double) =0.866025396499206845 d
std::cos(double)=0.866025396499206845 d -std::cos(float) =0.866025388240814209 f
cos(float) =0.866025396499206845 d +std::cos(float) =0.866025447845458984 f
cos(double) =0.866025396499206845 d std::cos(double)=0.866025396499206845 d
using namespace std; cos(float) =0.866025396499206845 d
-cos(float) =0.866025388240814209 f cos(double) =0.866025396499206845 d
+cos(float) =0.866025447845458984 f using namespace std;
cos(double) =0.866025396499206845 d -cos(float) =0.866025388240814209 f
``` +cos(float) =0.866025447845458984 f
cos(double) =0.866025396499206845 d
### 暗黙的なbool変換について ```
`-Wall -Wextra -Wconversion`としても、warningとならない
``` cpp ### 暗黙的なbool変換について
#include <iostream> `-Wall -Wextra -Wconversion`としても、warningとならない
#include <string> ``` cpp
#include <iostream>
int main(int argc, char* argv[]) { #include <string>
// trueなので注意(アドレス値が!=0であるので)
// NG int main(int argc, char* argv[]) {
bool x = []() { return false; }; // trueなので注意(アドレス値が!=0であるので)
// OK // NG
// bool x = []() { return false; }(); bool x = []() { return false; };
std::cout << x << std::endl; // OK
return 0; // bool x = []() { return false; }();
} std::cout << x << std::endl;
``` return 0;
}
### 暗黙的な型変換(float, double) ```
``` bash
g++ -std=c++11 a.cpp -Wall -Wextra -Wconversion ### 暗黙的な型変換(float, double)
``` ``` bash
g++ -std=c++11 a.cpp -Wall -Wextra -Wconversion
* `std::copy`を利用する場合、`std::vector<float>``std::vector<double>`では警告がでない ```
* `float``double`へ代入する際に警告がでないが、ユーザとしてはもともとdoubleで計算した結果を意図しているケースが多い
* `std::copy`を利用する場合、`std::vector<float>``std::vector<double>`では警告がでない
``` cpp * `float``double`へ代入する際に警告がでないが、ユーザとしてはもともとdoubleで計算した結果を意図しているケースが多い
#include <iterator>
#include <vector> ``` cpp
#include <iterator>
int main(int argc, const char* argv[]) { #include <vector>
(void)&argc;
(void)&argv; int main(int argc, const char* argv[]) {
(void)&argc;
{ (void)&argv;
std::vector<float> f_vec = {1.0f, 2.0f};
std::vector<double> d_vec = {0.0, 0.0}; {
std::vector<float> f_vec = {1.0f, 2.0f};
// no warning std::vector<double> d_vec = {0.0, 0.0};
std::copy(f_vec.begin(), f_vec.end(), d_vec.begin());
// no warning // no warning
std::copy(d_vec.begin(), d_vec.end(), f_vec.begin()); std::copy(f_vec.begin(), f_vec.end(), d_vec.begin());
} // no warning
std::copy(d_vec.begin(), d_vec.end(), f_vec.begin());
{ }
float f = 1.0f;
// 厳密には、ここで桁落ちしているわけではないので警告がでないと思われる {
// しかし、意図していない操作である float f = 1.0f;
double d = f; // 厳密には、ここで桁落ちしているわけではないので警告がでないと思われる
// warning: conversion to ‘float’ from ‘double’ may alter its value [-Wfloat-conversion] // しかし、意図していない操作である
float f2 = d; double d = f;
// warning: conversion to ‘float’ from ‘double’ may alter its value [-Wfloat-conversion]
(void)&f; float f2 = d;
(void)&d;
(void)&f2; (void)&f;
} (void)&d;
(void)&f2;
{ }
// warning: conversion to ‘float’ alters ‘double’ constant value [-Wfloat-conversion]
float f = 1.0f / 3.0; {
(void)&f; // warning: conversion to ‘float’ alters ‘double’ constant value [-Wfloat-conversion]
} float f = 1.0f / 3.0;
return 0; (void)&f;
} }
``` return 0;
}
## ファイル ```
### ファイルIOのベンチマーク
[c++ io benchmark]( https://gist.github.com/umaumax/1bde594a831c50f0d6d21a520209aad8 ) ## ファイル
### ファイルIOのベンチマーク
### ファイル/ディレクトリの存在確認 [c++ io benchmark]( https://gist.github.com/umaumax/1bde594a831c50f0d6d21a520209aad8 )
``` cpp
#include <fstream> ### ファイル/ディレクトリの存在確認
#include <string> ``` cpp
#include <fstream>
bool IsFileExist(const std::string &filename) { #include <string>
std::ifstream ifs(filename);
return ifs.is_open(); bool IsFileExist(const std::string &filename) {
} std::ifstream ifs(filename);
``` return ifs.is_open();
* ファイル/ディレクトリが存在: true }
* シンボリックリンクの場合はその参照先に依存 ```
* ファイル/ディレクトリが存在: true
### 🌟ファイルを読みたい * シンボリックリンクの場合はその参照先に依存
* [How to read a file from disk to std::vector\<uint8\_t> in C\+\+]( https://gist.github.com/looopTools/64edd6f0be3067971e0595e1e4328cbc )
* [c++ - How to read a binary file into a vector of unsigned chars - Stack Overflow]( https://stackoverflow.com/questions/15138353/how-to-read-a-binary-file-into-a-vector-of-unsigned-chars ) ### 🌟ファイルを読みたい
* [How to read a file from disk to std::vector\<uint8\_t> in C\+\+]( https://gist.github.com/looopTools/64edd6f0be3067971e0595e1e4328cbc )
* __なぜか、`std::istream_iterator`を利用すると、ファイルの読み込みサイズが小さくなってしまう現象が発生した__ * [c++ - How to read a binary file into a vector of unsigned chars - Stack Overflow]( https://stackoverflow.com/questions/15138353/how-to-read-a-binary-file-into-a-vector-of-unsigned-chars )
* __理由は`\n`をスキップして`std::vector`へ保存してしまうためである__
* `std::istreambuf_iterator`には`uint8_t`は指定できない * __なぜか、`std::istream_iterator`を利用すると、ファイルの読み込みサイズが小さくなってしまう現象が発生した__
* __理由は`\n`をスキップして`std::vector`へ保存してしまうためである__
``` cpp * `std::istreambuf_iterator`には`uint8_t`は指定できない
#include <fstream>
#include <iostream> ``` cpp
#include <iterator> #include <fstream>
#include <vector> #include <iostream>
#include <iterator>
// LoadFileGood_istreambuf_iteratorと比較して、2倍ほど遅い #include <vector>
std::vector<uint8_t> LoadFileGood(const std::string& filepath) {
std::ifstream file(filepath, std::ios::binary); // LoadFileGood_istreambuf_iteratorと比較して、2倍ほど遅い
if (file.fail()) { std::vector<uint8_t> LoadFileGood(const std::string& filepath) {
return std::vector<uint8_t>(); std::ifstream file(filepath, std::ios::binary);
} if (file.fail()) {
file.unsetf(std::ios::skipws); return std::vector<uint8_t>();
}
std::streampos fileSize; file.unsetf(std::ios::skipws);
file.seekg(0, std::ios::end);
fileSize = file.tellg(); std::streampos fileSize;
file.seekg(0, std::ios::beg); file.seekg(0, std::ios::end);
fileSize = file.tellg();
std::vector<uint8_t> vec; file.seekg(0, std::ios::beg);
vec.reserve(fileSize);
vec.insert(vec.begin(), std::istream_iterator<uint8_t>(file), std::vector<uint8_t> vec;
std::istream_iterator<uint8_t>()); vec.reserve(fileSize);
return vec; vec.insert(vec.begin(), std::istream_iterator<uint8_t>(file),
} std::istream_iterator<uint8_t>());
return vec;
// 🌟おすすめ }
std::vector<uint8_t> LoadFileGood_istreambuf_iterator(const std::string& filepath) {
std::ifstream is(filepath, std::ios::in | std::ios::binary); // 🌟おすすめ
std::istreambuf_iterator<char> start(is), end; std::vector<uint8_t> LoadFileGood_istreambuf_iterator(const std::string& filepath) {
std::vector<uint8_t> content(start, end); std::ifstream is(filepath, std::ios::in | std::ios::binary);
return content; std::istreambuf_iterator<char> start(is), end;
} std::vector<uint8_t> content(start, end);
return content;
std::vector<uint8_t> LoadFileBad_istream_iterator(const std::string& filepath) { }
std::ifstream is(filepath, std::ios::in | std::ios::binary);
std::istream_iterator<char> start(is), end; std::vector<uint8_t> LoadFileBad_istream_iterator(const std::string& filepath) {
std::vector<uint8_t> content(start, end); std::ifstream is(filepath, std::ios::in | std::ios::binary);
return content; std::istream_iterator<char> start(is), end;
} std::vector<uint8_t> content(start, end);
return content;
int main(int argc, const char* argv[]) { }
std::string filepath("./main.cpp");
int main(int argc, const char* argv[]) {
{ std::string filepath("./main.cpp");
auto goodVec = LoadFileGood(filepath);
std::cout << goodVec.size() << std::endl; {
for (auto& v : goodVec) { auto goodVec = LoadFileGood(filepath);
std::cout << v; std::cout << goodVec.size() << std::endl;
} for (auto& v : goodVec) {
std::cout << std::endl; std::cout << v;
} }
std::cout << std::endl;
{ }
auto goodVec = LoadFileGood_istreambuf_iterator(filepath);
std::cout << goodVec.size() << std::endl; {
for (auto& v : goodVec) { auto goodVec = LoadFileGood_istreambuf_iterator(filepath);
std::cout << v; std::cout << goodVec.size() << std::endl;
} for (auto& v : goodVec) {
std::cout << std::endl; std::cout << v;
} }
std::cout << std::endl;
{ }
auto badVec = LoadFileBad_istream_iterator(filepath);
std::cout << badVec.size() << std::endl; {
for (auto& v : badVec) { auto badVec = LoadFileBad_istream_iterator(filepath);
std::cout << v; std::cout << badVec.size() << std::endl;
} for (auto& v : badVec) {
std::cout << std::endl; std::cout << v;
} }
return 0; std::cout << std::endl;
} }
``` return 0;
}
### ファイルをmv(rename)する ```
* [std::rename \- cppreference\.com]( https://en.cppreference.com/w/cpp/io/c/rename ) ### ファイルをmv(rename)する
* [FIO10\-C\. rename\(\) 関数の使用に注意する]( https://www.jpcert.or.jp/sc-rules/c-fio10-c.html )
* [std::rename \- cppreference\.com]( https://en.cppreference.com/w/cpp/io/c/rename )
移植性を考慮した場合,変更先が空ディレクトリは削除できるのでrename時にエラーが起こらないが,空ディレクトリではない場合はremoveが失敗する * [FIO10\-C\. rename\(\) 関数の使用に注意する]( https://www.jpcert.or.jp/sc-rules/c-fio10-c.html )
``` cpp 移植性を考慮した場合,変更先が空ディレクトリは削除できるのでrename時にエラーが起こらないが,空ディレクトリではない場合はremoveが失敗する
#include <cstdio>
#include <cstring> ``` cpp
#include <fstream> #include <cstdio>
#include <iostream> #include <cstring>
#include <string> #include <fstream>
#include <iostream>
int main() { #include <string>
char oldname[] = "a.txt";
char newname[] = "b.txt"; int main() {
char oldname[] = "a.txt";
// NOTE: char newname[] = "b.txt";
// remove file for avoiding std::rename implementation-defined
// If new_filename exists, the behavior is implementation-defined. // NOTE:
bool is_exist_newname_file = [](const std::string& filename) { // remove file for avoiding std::rename implementation-defined
std::ifstream ifs(filename); // If new_filename exists, the behavior is implementation-defined.
return ifs.is_open(); bool is_exist_newname_file = [](const std::string& filename) {
}(newname); std::ifstream ifs(filename);
if (is_exist_newname_file) { return ifs.is_open();
int remove_result = std::remove(newname); }(newname);
if (remove_result != 0) { if (is_exist_newname_file) {
std::cerr << "Error remove file '" << newname int remove_result = std::remove(newname);
<< "': " << std::strerror(errno) << std::endl; if (remove_result != 0) {
return 1; std::cerr << "Error remove file '" << newname
} << "': " << std::strerror(errno) << std::endl;
} return 1;
}
int rename_result = rename(oldname, newname); }
if (rename_result != 0) {
std::cerr << "Error renaming file '" << oldname << "' to '" << newname int rename_result = rename(oldname, newname);
<< "': " << std::strerror(errno) << std::endl; if (rename_result != 0) {
return 1; std::cerr << "Error renaming file '" << oldname << "' to '" << newname
} << "': " << std::strerror(errno) << std::endl;
return 0; return 1;
} }
``` return 0;
}
### ディレクトリ名取得/ディレクトリのfsyncを行う例 ```
``` cpp
#include <errno.h> // strerror ### ディレクトリ名取得/ディレクトリのfsyncを行う例
#include <fcntl.h> // O_DIRECTORY, O_RDONLY ``` cpp
#include <unistd.h> // fsync, close #include <errno.h> // strerror
#include <fcntl.h> // O_DIRECTORY, O_RDONLY
#include <cstring> // strerror #include <unistd.h> // fsync, close
#include <iostream>
#include <string> #include <cstring> // strerror
#include <tuple> #include <iostream>
#include <string>
std::string Dirname(const std::string& filepath) { #include <tuple>
const size_t pos = filepath.find_last_of("/");
if (std::string::npos == pos) { std::string Dirname(const std::string& filepath) {
return "."; const size_t pos = filepath.find_last_of("/");
} if (std::string::npos == pos) {
const std::string dirname = filepath.substr(0, pos); return ".";
if (dirname == "") { }
return "/"; const std::string dirname = filepath.substr(0, pos);
} if (dirname == "") {
return dirname; return "/";
} }
std::tuple<bool, std::string> SyncDir(const std::string& dirpath) { return dirname;
std::string error_message{}; }
int fd = open(dirpath.c_str(), O_DIRECTORY | O_RDONLY); std::tuple<bool, std::string> SyncDir(const std::string& dirpath) {
if (fd == -1) { std::string error_message{};
error_message = std::string(strerror(errno)) + ": dirpath = " + dirpath; int fd = open(dirpath.c_str(), O_DIRECTORY | O_RDONLY);
return std::make_tuple(false, error_message); if (fd == -1) {
} error_message = std::string(strerror(errno)) + ": dirpath = " + dirpath;
int fsync_ret = fsync(fd); return std::make_tuple(false, error_message);
if (fsync_ret == -1) { }
error_message += std::string(strerror(errno)) + ": dirpath = " + dirpath; int fsync_ret = fsync(fd);
} if (fsync_ret == -1) {
int close_ret = close(fd); error_message += std::string(strerror(errno)) + ": dirpath = " + dirpath;
if (close_ret == -1) { }
error_message += std::string(strerror(errno)) + ": dirpath = " + dirpath; int close_ret = close(fd);
} if (close_ret == -1) {
return std::make_tuple(error_message.empty(), error_message); error_message += std::string(strerror(errno)) + ": dirpath = " + dirpath;
} }
int main(int argc, char* argv[]) { return std::make_tuple(error_message.empty(), error_message);
bool ret{}; }
std::string error_message{}; int main(int argc, char* argv[]) {
std::tie(ret, error_message) = SyncDir("target_dir"); bool ret{};
if (!ret) { std::string error_message{};
std::cerr << error_message << std::endl; std::tie(ret, error_message) = SyncDir("target_dir");
} if (!ret) {
return 0; std::cerr << error_message << std::endl;
} }
``` return 0;
}
### ファイルディスクリプタ ```
`<unistd.h>` ### ファイルディスクリプタ
| プリプロセッサシンボル | value | `<unistd.h>`
| -------- | -------- |
| STDIN_FILENO | 0 | | プリプロセッサシンボル | value |
| STDOUT_FILENO | 1 | | -------- | -------- |
| STDERR_FILENO | 2 | | STDIN_FILENO | 0 |
| STDOUT_FILENO | 1 |
[Man page of STDIN]( https://linuxjm.osdn.jp/html/LDP_man-pages/man3/stdin.3.html ) | STDERR_FILENO | 2 |
### 一時ファイル作成のイディオム [Man page of STDIN]( https://linuxjm.osdn.jp/html/LDP_man-pages/man3/stdin.3.html )
`open()`してから`unlink()`することで,削除し忘れを防ぐことができる ### 一時ファイル作成のイディオム
[ファイルを open したまま各種システムコールを呼び出すとどうなるか \| ゴミ箱]( https://53ningen.com/file-open-rename-unlink/ ) `open()`してから`unlink()`することで,削除し忘れを防ぐことができる
### `close(2)` [ファイルを open したまま各種システムコールを呼び出すとどうなるか \| ゴミ箱]( https://53ningen.com/file-open-rename-unlink/ )
[【C言語】close\(\)失敗時にリトライするのは危険という話 \- Qiita]( https://qiita.com/_ydah/items/c5f6b42dbfb88c2fae1f )
### `close(2)`
### ファイルをseekして使い続ける例 [【C言語】close\(\)失敗時にリトライするのは危険という話 \- Qiita]( https://qiita.com/_ydah/items/c5f6b42dbfb88c2fae1f )
``` cpp ### ファイルをseekして使い続ける例
ofs.clear();
ofs.seekg(0, std::ios::beg); ``` cpp
``` ofs.clear();
`std::ifstream`をずっと繰り返していると、メモリリークがあるのかもしれないと考えての実験用だが,特に問題なし ofs.seekg(0, std::ios::beg);
```
``` cpp `std::ifstream`をずっと繰り返していると、メモリリークがあるのかもしれないと考えての実験用だが,特に問題なし
#include <fstream>
#include <iostream> ``` cpp
#include <sstream> #include <fstream>
#include <string> #include <iostream>
#include <sstream>
int main(int argc, char* argv[]) { #include <string>
std::ifstream ifs("./main.cpp");
const int buf_size = 80; int main(int argc, char* argv[]) {
char line[buf_size]; std::ifstream ifs("./main.cpp");
while (true) { const int buf_size = 80;
ifs.getline(line, buf_size); char line[buf_size];
ifs.clear(); while (true) {
ifs.seekg(0, std::ios::beg); ifs.getline(line, buf_size);
} ifs.clear();
return 0; ifs.seekg(0, std::ios::beg);
} }
``` return 0;
}
## リストに処理をした結果に対して、何らかの処理をするが、共通する結果に対しては1回でまとめてもよい場合の例 ```
``` cpp ## リストに処理をした結果に対して、何らかの処理をするが、共通する結果に対しては1回でまとめてもよい場合の例
#include <iostream>
#include <set> ``` cpp
#include <string> #include <iostream>
#include <vector> #include <set>
#include <string>
int main(int argc, char *argv[]) { #include <vector>
std::vector<std::string> hoge_list = {
"hoge", int main(int argc, char *argv[]) {
"fuga", std::vector<std::string> hoge_list = {
"hoge", "hoge",
}; "fuga",
std::set<std::string> hoge_set = {}; "hoge",
for (auto &&hoge : hoge_list) { };
hoge_set.insert(hoge); std::set<std::string> hoge_set = {};
} for (auto &&hoge : hoge_list) {
for (auto &&hoge : hoge_set) { hoge_set.insert(hoge);
std::cout << hoge << std::endl; }
} for (auto &&hoge : hoge_set) {
return 0; std::cout << hoge << std::endl;
} }
``` return 0;
}
## ODR(one definition rule) ```
* ヘッダに実装を記述する際に,通常の関数は`inline`を付与する必要がある
* template関数はinline不要 ## ODR(one definition rule)
* ただし,特殊化されたtemplateではinlineは必要 * ヘッダに実装を記述する際に,通常の関数は`inline`を付与する必要がある
* template関数はinline不要
## assert * ただし,特殊化されたtemplateではinlineは必要
[ERR06\-C\. assert\(\) と abort\(\) の終了動作を理解する]( https://www.jpcert.or.jp/sc-rules/c-err06-c.html )
## assert
* `assert()``aboort()`を呼び出して、`SIGABRT`を送信するので、gdbで補足できる [ERR06\-C\. assert\(\) と abort\(\) の終了動作を理解する]( https://www.jpcert.or.jp/sc-rules/c-err06-c.html )
* `NDEBUG`有効時にも、`gdb`でbacktraceを表示したい場合は`abort()`を利用すれば良い
* `assert()``aboort()`を呼び出して、`SIGABRT`を送信するので、gdbで補足できる
## backtrace * `NDEBUG`有効時にも、`gdb`でbacktraceを表示したい場合は`abort()`を利用すれば良い
[\[Linux\]\[C/C\+\+\] backtrace取得方法まとめ \- Qiita]( https://qiita.com/koara-local/items/012b917111a96f76d27c )
## backtrace
* glibc backtraceは`-ggdb3`では関数名が出力できず、`-rdynamic`を付加して動的ライブラリ化する必要があった [\[Linux\]\[C/C\+\+\] backtrace取得方法まとめ \- Qiita]( https://qiita.com/koara-local/items/012b917111a96f76d27c )
* 行数までは取得できない
* glibc backtraceは`-ggdb3`では関数名が出力できず、`-rdynamic`を付加して動的ライブラリ化する必要があった
### libunwind * 行数までは取得できない
``` bash
# for ubuntu ### libunwind
sudo apt-get install libunwind-dev ``` bash
``` # for ubuntu
libunwindを利用すると、`-ggdb3``-rdynamic`なしでも関数名を取得可能 sudo apt-get install libunwind-dev
```
Mac OS Xではclangに取り込まれているので、そのまま利用可能(`-lunwind`も不要) libunwindを利用すると、`-ggdb3``-rdynamic`なしでも関数名を取得可能
see: [llvm\-project/libunwind\.h at main · llvm/llvm\-project]( https://github.com/llvm/llvm-project/blob/main/libunwind/include/libunwind.h )
Mac OS Xではclangに取り込まれているので、そのまま利用可能(`-lunwind`も不要)
## clang++ see: [llvm\-project/libunwind\.h at main · llvm/llvm\-project]( https://github.com/llvm/llvm-project/blob/main/libunwind/include/libunwind.h )
### Ubuntuにてclang++で`cannot find iostream`となるときに、下記で直る
[c++ - clang++ cannot find iostream - Ask Ubuntu]( https://askubuntu.com/questions/1449769/clang-cannot-find-iostream ) ## clang++
### Ubuntuにてclang++で`cannot find iostream`となるときに、下記で直る
``` bash [c++ - clang++ cannot find iostream - Ask Ubuntu]( https://askubuntu.com/questions/1449769/clang-cannot-find-iostream )
sudo apt install -y g++-12
``` ``` bash
sudo apt install -y g++-12
## clang-format ```
ヘッダのソートなどの優先順位についての例: [clang-formatで*.generated.hを勝手にソートさせない方法]( https://zenn.dev/sgthr7/articles/14271d56253e7a )
## clang-format
## switchのcase中に変数宣言をするときの注意 ヘッダのソートなどの優先順位についての例: [clang-formatで*.generated.hを勝手にソートさせない方法]( https://zenn.dev/sgthr7/articles/14271d56253e7a )
caseごとではなく、switch単位でスコープとなるので、switchの範囲外で予め宣言するか、case内部に明示的にブロックを設ける必要がある
* [c\+\+ \- Getting a bunch of crosses initialization error \- Stack Overflow]( https://stackoverflow.com/questions/11578936/getting-a-bunch-of-crosses-initialization-error/11578973 ) ## switchのcase中に変数宣言をするときの注意
* [c\+\+ \- What are the signs of crosses initialization? \- Stack Overflow]( https://stackoverflow.com/questions/2392655/what-are-the-signs-of-crosses-initialization ) caseごとではなく、switch単位でスコープとなるので、switchの範囲外で予め宣言するか、case内部に明示的にブロックを設ける必要がある
* [c\+\+ \- Getting a bunch of crosses initialization error \- Stack Overflow]( https://stackoverflow.com/questions/11578936/getting-a-bunch-of-crosses-initialization-error/11578973 )
## コピーやムーブをした場合でもデストラクタが呼ばれる * [c\+\+ \- What are the signs of crosses initialization? \- Stack Overflow]( https://stackoverflow.com/questions/2392655/what-are-the-signs-of-crosses-initialization )
``` cpp
{ ## コピーやムーブをした場合でもデストラクタが呼ばれる
Hoge hoge;// コンストラクタ ``` cpp
hoge = Hoge("hello");// コンストラクタ, デストラクタ {
// デストラクタ Hoge hoge;// コンストラクタ
} hoge = Hoge("hello");// コンストラクタ, デストラクタ
``` // デストラクタ
}
[自作クラスをムーブする \- Qiita]( https://qiita.com/termoshtt/items/3397c149bf2e4ce07e6c ) ```
## 実行時(共有ライブラリのロード時)に`undefined symbol: _ZTI8XXXClass(typeinfo for XXXClass)` [自作クラスをムーブする \- Qiita]( https://qiita.com/termoshtt/items/3397c149bf2e4ce07e6c )
``` cpp
class XXXClass { ## 実行時(共有ライブラリのロード時)に`undefined symbol: _ZTI8XXXClass(typeinfo for XXXClass)`
virtual void foo() = 0; ``` cpp
}; class XXXClass {
``` virtual void foo() = 0;
ここで`=0`としない場合は実体を定義する必要がある };
```
ビルドは問題なく通り、実行時に発覚することが問題点 ここで`=0`としない場合は実体を定義する必要がある
たしかに、共有ライブラリを`nm`コマンドで見てみると`U`となっている ビルドは問題なく通り、実行時に発覚することが問題点
* [c\+\+ \- What is an undefined reference/unresolved external symbol error and how do I fix it? \- Stack Overflow]( https://stackoverflow.com/questions/12573816/what-is-an-undefined-reference-unresolved-external-symbol-error-and-how-do-i-fix ) たしかに、共有ライブラリを`nm`コマンドで見てみると`U`となっている
* [Undefined symbols for architecture x86\_64:の原因(複数) \| MaryCore]( https://marycore.jp/prog/xcode/undefined-symbols-for-architecture-x86-64/ )
* [c\+\+ \- What is an undefined reference/unresolved external symbol error and how do I fix it? \- Stack Overflow]( https://stackoverflow.com/questions/12573816/what-is-an-undefined-reference-unresolved-external-symbol-error-and-how-do-i-fix )
## 読み物 * [Undefined symbols for architecture x86\_64:の原因(複数) \| MaryCore]( https://marycore.jp/prog/xcode/undefined-symbols-for-architecture-x86-64/ )
* [UNIX上でのC\+\+ソフトウェア設計の定石 \(1\) \- memologue]( https://yupo5656.hatenadiary.org/entry/20040712/p1 )
* [UNIX上でのC\+\+ソフトウェア設計の定石 \(2\) \- memologue]( https://yupo5656.hatenadiary.org/entry/20040712/p2 ) ## 読み物
* [UNIX上でのC\+\+ソフトウェア設計の定石 \(3\) \- memologue]( https://yupo5656.hatenadiary.org/entry/20040715/p1 ) * [UNIX上でのC\+\+ソフトウェア設計の定石 \(1\) \- memologue]( https://yupo5656.hatenadiary.org/entry/20040712/p1 )
* [UNIX上でのC\+\+ソフトウェア設計の定石 \(4\) \- memologue]( https://yupo5656.hatenadiary.org/entry/20040724/p1 ) * [UNIX上でのC\+\+ソフトウェア設計の定石 \(2\) \- memologue]( https://yupo5656.hatenadiary.org/entry/20040712/p2 )
* [UNIX上でのC\+\+ソフトウェア設計の定石 \(5\) \- memologue]( https://yupo5656.hatenadiary.org/entry/20040725/p2 ) * [UNIX上でのC\+\+ソフトウェア設計の定石 \(3\) \- memologue]( https://yupo5656.hatenadiary.org/entry/20040715/p1 )
* [UNIX上でのC\+\+ソフトウェア設計の定石 \(4\) \- memologue]( https://yupo5656.hatenadiary.org/entry/20040724/p1 )
## tips * [UNIX上でのC\+\+ソフトウェア設計の定石 \(5\) \- memologue]( https://yupo5656.hatenadiary.org/entry/20040725/p2 )
### std::endl
`std::endl`は実行毎に出力バッファの内容を逐一flushするので、実行時オーバーヘッドを減らすため、ループの途中などflushの必要がない箇所では改行文字を使用した方が良い。 ## tips
### std::endl
``` cpp `std::endl`は実行毎に出力バッファの内容を逐一flushするので、実行時オーバーヘッドを減らすため、ループの途中などflushの必要がない箇所では改行文字を使用した方が良い。
for (auto i = start; i != end; ++i) std::cout << i << std::endl;
// => ``` cpp
for (auto i = start; i != end; ++i) std::cout << i << '\n'; for (auto i = start; i != end; ++i) std::cout << i << std::endl;
``` // =>
for (auto i = start; i != end; ++i) std::cout << i << '\n';
### std::map ```
`std::map`系のコンテナに対して`[]`演算子を用いて存在しないキーを参照した場合、valueをデフォルト値で初期化した上で要素を新規作成するという挙動である
### std::map
### 出力用の参照渡し引数ではなく、返り値で出力を返すことが推奨される `std::map`系のコンテナに対して`[]`演算子を用いて存在しないキーを参照した場合、valueをデフォルト値で初期化した上で要素を新規作成するという挙動である
``` cpp ### 出力用の参照渡し引数ではなく、返り値で出力を返すことが推奨される
void get_pos(Pos &out_pos) {
out_pos.x = 1; ``` cpp
out_pos.y = 2; void get_pos(Pos &out_pos) {
} out_pos.x = 1;
``` out_pos.y = 2;
=> }
``` cpp ```
struct Pos { =>
int x, y; ``` cpp
}; struct Pos {
int x, y;
Pos get_pos() { };
return {1, 2};
} Pos get_pos() {
``` return {1, 2};
}
戻り値の構造体は自動的にmoveされるので、実行時オーバーヘッドは発生しない。 ```
理想的には、RustのようにResult型を利用すると良い(ただし、デファクトスタンダードなライブラリが不明...) 戻り値の構造体は自動的にmoveされるので、実行時オーバーヘッドは発生しない。
### 基底クラスとなるクラスのデストラクタにはvirtualを付与すること 理想的には、RustのようにResult型を利用すると良い(ただし、デファクトスタンダードなライブラリが不明...)
付与しないと継承先クラスを基底クラスへキャストしたときに呼び出されない
### 基底クラスとなるクラスのデストラクタにはvirtualを付与すること
``` cpp 付与しないと継承先クラスを基底クラスへキャストしたときに呼び出されない
#include <iostream>
``` cpp
class Animal { #include <iostream>
public:
void foo() { std::cout << "do foo" << std::endl; } class Animal {
public:
~Animal() { std::cout << "called base animal destructor" << std::endl; } void foo() { std::cout << "do foo" << std::endl; }
};
~Animal() { std::cout << "called base animal destructor" << std::endl; }
class Dog : public Animal { };
public:
~Dog() { std::cout << "called derived dog destructor" << std::endl; } class Dog : public Animal {
}; public:
~Dog() { std::cout << "called derived dog destructor" << std::endl; }
class VirtualAnimal { };
public:
void foo() { std::cout << "do virtual foo" << std::endl; } class VirtualAnimal {
public:
virtual ~VirtualAnimal() { void foo() { std::cout << "do virtual foo" << std::endl; }
std::cout << "called base virtual animal destructor" << std::endl;
} virtual ~VirtualAnimal() {
}; std::cout << "called base virtual animal destructor" << std::endl;
}
class VirtualDog : public VirtualAnimal { };
public:
~VirtualDog() { class VirtualDog : public VirtualAnimal {
std::cout << "called derived virtual dog destructor" << std::endl; public:
} ~VirtualDog() {
}; std::cout << "called derived virtual dog destructor" << std::endl;
}
int main(int argc, const char* argv[]) { };
std::cout << "start" << std::endl;
int main(int argc, const char* argv[]) {
std::cout << "# dog" << std::endl; std::cout << "start" << std::endl;
{
Dog dog; std::cout << "# dog" << std::endl;
dog.foo(); {
} Dog dog;
std::cout << "# virtual dog" << std::endl; dog.foo();
{ }
VirtualDog dog; std::cout << "# virtual dog" << std::endl;
dog.foo(); {
} VirtualDog dog;
std::cout << "# derived dog" << std::endl; dog.foo();
{ }
auto derived = std::make_unique<Dog>(); std::cout << "# derived dog" << std::endl;
std::unique_ptr<Animal> p = std::move(derived); {
p->foo(); auto derived = std::make_unique<Dog>();
} std::unique_ptr<Animal> p = std::move(derived);
std::cout << "# derived virtual dog" << std::endl; p->foo();
{ }
auto derived = std::make_unique<VirtualDog>(); std::cout << "# derived virtual dog" << std::endl;
std::unique_ptr<VirtualAnimal> p = std::move(derived); {
p->foo(); auto derived = std::make_unique<VirtualDog>();
} std::unique_ptr<VirtualAnimal> p = std::move(derived);
std::cout << "end" << std::endl; p->foo();
return 0; }
} std::cout << "end" << std::endl;
``` return 0;
}
``` cpp ```
start
# dog ``` cpp
do foo start
called derived dog destructor # dog
called base animal destructor do foo
# virtual dog called derived dog destructor
do virtual foo called base animal destructor
called derived virtual dog destructor # virtual dog
called base virtual animal destructor do virtual foo
# derived dog called derived virtual dog destructor
do foo called base virtual animal destructor
called base animal destructor <=== 🔥 継承先のデストラクタが呼び出されていないことがわかる # derived dog
# derived virtual dog do foo
do virtual foo called base animal destructor <=== 🔥 継承先のデストラクタが呼び出されていないことがわかる
called derived virtual dog destructor # derived virtual dog
called base virtual animal destructor do virtual foo
end called derived virtual dog destructor
``` called base virtual animal destructor
end
```
### 🔥クラスのメンバ変数はコンストラクタ引数の順序ではなく、メンバの定義順に初期化される
``` cpp ### 🔥クラスのメンバ変数はコンストラクタ引数の順序ではなく、メンバの定義順に初期化される
#include <iostream>
``` cpp
class ViewGood { #include <iostream>
public:
ViewGood(int *start, std::size_t size) class ViewGood {
: m_start{start}, m_end{m_start + size} { public:
std::cout << "view constructor: " << m_start << "," << m_end << '\n'; ViewGood(int *start, std::size_t size)
} : m_start{start}, m_end{m_start + size} {
std::cout << "view constructor: " << m_start << "," << m_end << '\n';
private: }
int *m_start;
int *m_end; private:
}; int *m_start;
int *m_end;
class ViewBad { };
public:
ViewBad(int *start, std::size_t size) class ViewBad {
: m_start{start}, m_end{m_start + size} { public:
std::cout << "view constructor: " << m_start << "," << m_end << '\n'; ViewBad(int *start, std::size_t size)
} : m_start{start}, m_end{m_start + size} {
std::cout << "view constructor: " << m_start << "," << m_end << '\n';
private: }
int *m_end;
int *m_start; private:
}; int *m_end;
int *m_start;
int main(int argc, const char *argv[]) { };
{ ViewGood v(nullptr, 1); }
{ ViewBad v(nullptr, 1); } int main(int argc, const char *argv[]) {
return 0; { ViewGood v(nullptr, 1); }
} { ViewBad v(nullptr, 1); }
``` return 0;
}
``` bash ```
$ g++ -std=c++20 main.cpp
main.cpp:18:31: warning: field 'm_start' is uninitialized when used here [-Wuninitialized] ``` bash
: m_start{start}, m_end{m_start + size} { $ g++ -std=c++20 main.cpp
^ main.cpp:18:31: warning: field 'm_start' is uninitialized when used here [-Wuninitialized]
$ ./a.out : m_start{start}, m_end{m_start + size} {
view constructor: 0x0,0x4 ^
view constructor: 0x0,0x1987fe3ca $ ./a.out
``` view constructor: 0x0,0x4
view constructor: 0x0,0x1987fe3ca
### 🔥C++において引数の評価順は規定されておらず、評価順によって結果が変わるコードの動作は未定義である ```
引数で実行する関数の依存関係がなく順不同で実行されても問題がないようにすること ### 🔥C++において引数の評価順は規定されておらず、評価順によって結果が変わるコードの動作は未定義である
### [syncstream - cpprefjp C++日本語リファレンス]( https://cpprefjp.github.io/reference/syncstream.html ) 引数で実行する関数の依存関係がなく順不同で実行されても問題がないようにすること
### [syncstream - cpprefjp C++日本語リファレンス]( https://cpprefjp.github.io/reference/syncstream.html )
[C++20便利機能の紹介:同期出力ストリーム std::osyncstream #C++ - Qiita]( https://qiita.com/yohhoy/items/b0fa779176d5debcd09e ) [C++20便利機能の紹介:同期出力ストリーム std::osyncstream #C++ - Qiita]( https://qiita.com/yohhoy/items/b0fa779176d5debcd09e )
\ No newline at end of file