コンストラクタにexplicitを付けていない時に起きる混乱の例の一つ

## TL;DR

(copy/move ctor以外の)引数が一つのコンストラクタには（特別な理由がなければ(@yumetodo氏のコメント参照)）`explicit`をつけろ

... いつもどおりだな！（C++erが口を揃えて常に言い続けていることだし）

## 発端

先程、[AtCoderABC172の解説放送(2:51:00へのリンク)](https://youtu.be/v8ppNGf49Nk?t=10259)を見ていたときのこと

競技プログラミングの事はこの記事の本筋ではないので省くが、画面にはこんなコードが写っていた(説明のため重要でない部分を変更/省略してます)

```c++
struct mint {
    int x;
    mint(int x=0) : x(x) {}
    mint operator-() const { ...
    ...
};
std::istream& operator>>(std::istream& is, const mint& a) {
    return is >> a.x;
}
std::ostream& operator<<(std::ostream& os, const mint& a) {
    return os << a.x;
}
```

自分はこのコードをよく見ていなかった[^1]ので気づかなかったが、youtube liveのコメントで　「`>>`のオーバーロードにconstがついてる」　という指摘がされた

[^1]: 以前から公開されているライブラリなので

確かによく見ると右オペランドに非const参照を取るべきストリーム入力のオーバーロードが`const&`で定義されている

...

### なんでこれ、コンパイル通ってるの？

`operator>>(std::istream&, const int&)`なんてオーバーロードは存在しないはずなので、コンパイルエラーになるはずだ　何かがおかしい

（このオーバーロード全然使ってないみたいで、書いた人(snukeさん)も問題に気付いていなかったようです）

## 調べてみる

重要な部分以外を省いた+検証用のmainを追加したコードがこちら

```c++
#include <iostream>

struct Hoge {
    int x;
    Hoge(int y) : x(y) {}
};
std::istream& operator>>(std::istream& is, const Hoge& val) {
    return is >> val.x;
}

int main() {
    Hoge x{3};
    std::cin >> x;
}
```
[wandbox](https://wandbox.org/permlink/jPMWJh5nrILlHTl3)

**全くなぜだかコンパイルが通る**
**そして実行時にセグフォる**

意味がわからない

一応、`val.x`が`const`になっていることを確認してみる

```c++
std::istream& operator>>(std::istream& is, const Hoge& val) {
    static_assert(std::is_same_v<decltype((val.x)), const int&>);
    return is >> val.x;
}
```
[wandbox](https://wandbox.org/permlink/jNrsjeZQM7x4a1hp)
(`decltype`が二重カッコの理由は[こちら](https://cpprefjp.github.io/lang/cpp11/decltype.html))

**コンパイルが通る**

意味がわからない

## コンパイラを変えてみた

ここまでgccを使っていたので、エラーメッセージが解りやすいことの多いclangでコンパイルしてみる
[wandbox](https://wandbox.org/permlink/IVfrbuK01BtrTPFJ)

**コンパイルが**..ん？よく見ると警告が出ている（結局コンパイルは通ってる）

```
prog.cc:8:60: warning: all paths through this function will call itself [-Winfinite-recursion]
std::istream& operator>>(std::istream& is, const Hoge& val) {
```

...無限再帰????

つまり`is >> val.x;`が`operator>>(std::istream&, const Hoge&)`を呼び出しているということだ

え？`val.x`は`const int&`だってさっき確認したし、
`operator>>(std::istream&, const int&)`なんてオーバーロードは存在しないはずｄ...あ゛あ゛あ゛あ゛あ゛

```c++
Hoge(int y) : x(y) {}
```

こいつだああぁぁぁ

### つまり

1. `operator>>(std::istream&, const int&)`というオーバーロードは存在しない
1. `const int&`から暗黙に変換可能な型からオーバーロード先が検索される
1. `const int&`は`Hoge`に暗黙に変換できる（`is >> Hoge(val.x);`みたいにできる）
1. このままでは`>>`の右オペランドは`Hoge`だが、`Hoge`は`const Hoge&`で束縛できる（const参照は右辺値を束縛できる）
1. よって `is >> val.x;`で`operator>>(std::istream&, const Hoge&)`が呼び出されることで無限に再帰が発生する(今回は関係ないけど多分UB)

というわけで、コンストラクタに`explicit`[^2]をつけることで無事にコンパイルエラーにできました
[wandbox](https://wandbox.org/permlink/iTZLwiBDJWAwgC0M)

[^2]: 一応説明しておくと、これを付けたコンストラクタや変換オーバーロード(`operator int()`とか)は今回みたいな暗黙の変換がなされることがなくなります。詳しくはhttps://ja.cppreference.com/w/cpp/language/explicit

何、コンパイルエラーの量が多いだって？だって当然だろ？C++なら！

## そんなわけで

引数が1つのコンストラクタには可能な限り`explicit`を付けましょう

一応、`explicit`がないバージョンだと

```c++
mint x = 0;
x += mint(2); // こう書くのが本来だが
x += 2; // こうも書ける
```

みたいな記述が可能になるので付けないことにも利点があるように見えるが、変な間違いを防ぐためには`explicit`を付けておいたほうがいいだろう

explicitを付けた場合は上のコードみたいに`+= mint(2)`と毎回コンストラクタを明示的に呼ぶか、演算用のオーバーロードに整数型との演算を追加しておけば良い（ただし結構な数になる[^3]）
今回はコピペして使うライブラリということであまりに長くなるのは嬉しくないだろうから、私は前者をおすすめする

[^3]: 各2項演算ごとに(mint,mint), (mint, int), (int, mint)で3つ、各複合代入演算ごとに(mint, mint), (mint, int) で2つ必要になる。boostを使えばある程度省略できるとは思うが、初心者向けの解説だしメタプログラミングはあまり喜ばれないだろう