template 引数の `typname T` で `T` が参照型の場合に `new T()` とすると何が起こるの？

## 注意

調査しているうちに、表題と異なる結果になりまして、もし表題のことを実施すると悲惨なことになります。
（って、コンパイルエラーになるだけですが）
ただ、 `右辺値` とかの理解が進むと、同じケースでハマるところかもだし、一応オチもありましたので、このまま公開します。

## ことの発端

ウチの開発って `JSON` と戯れることが多く、20年くらい前から自作の `JSON` ライブラリを使っていたのですが、結構年季モノ [^1] でして、そろそろ刷新しようと作り変えてみました。[^2]
ひとまず、完成して簡単なテストもパスしたので、このまま業務で使い始めようと思ったのですが、「こう書いた方が良いのかな？」的な箇所がそのままになっていてモヤモヤしていたので、記事にしつつ記録に残そうと思いました。

[^1]: 年季モノというか、`template` の使い方が良く分かっていない若輩者が雰囲気で書いちゃったんだけど、実運用されちゃってよもや手を入れる隙間が無くなってしまったのです。

[^2]: 今となっては様々な `JSON` ライブラリが公開されていますが、`xml` に比べて `JSON` は規模的に小さなライブラリだし、何より作ると楽しいしって事で今回も自作としました。

## 問題の箇所

https://github.com/CODIANZ/cpp-json/blob/master/include/cppjson/json.h

問題の箇所は、 `json.h` の 174行目付近の　`set()` という関数です。（166行目付近の `set()` もありますが、174行目付近の `set()` の方が `new` が登場していて一番モヤッとレベルが高いです）

```cpp
template <typename T, std::enable_if_t<value_type_traits<T>::available && std::is_class<T>::value, bool> = true>
void set(T value) {
  destruct_value();
  m_value_type = value_type_traits<T>::type;
  using TT = typename std::remove_reference<T>::type;
  *reinterpret_cast<TT**>(&m_content) = new TT(std::forward<T>(value));
}
```

この `set()` 関数は `SFINAE` を使って利用可能な型を選別して呼び出されます。

1. `destruct_value()` で現在保有している値を破棄する。
1. `m_value_type` に型情報を保持する。
1. 次行ので `new` をするため `T` の参照を外して `TT` とする。
1. `TT` を `new` して値を保有する。

ここで「`T` を使って `new` しても良いのか？」ということで、<b>気持ち悪いから</b> `TT` を定義して `new` をしましたが、ここが今回のモヤットポイントです。


## 問題を簡略化してみる

```cpp
/** T を使ってそのまま new をする */
template <typename T> auto foo(T src) {
  return new T(std::forward<T>(src));
}

/** T の参照を外して new をする */
template <typename T> auto bar(T src) {
  using TT = typename std::remove_reference<T>::type;
  return new TT(std::forward<T>(src));
}
```

で、この子達に対して `右辺値` を与えてみます。

```cpp
int main() {
  auto f = foo(std::string("foo"));
  /** f は std::string* なの？ std::string&&* なの？　それとも何なの？ */
  std::cout << std::is_same<decltype(f), std::string*>::value << std::endl;
}
```
```:結果
1
```

はい、`std::string*` でした。
同様に b に対しても同様に

```cpp
int main() {
  auto b = bar(std::string("bar"));
  /** b は std::string* だよね？ */
  std::cout << std::is_same<decltype(f), std::string*>::value << std::endl;
}
```
```:結果
1
```

うん。期待通り。

## 考察

まず元のコードは

```cpp:元のコード
template <typename T> auto foo(T src) {
  return new T(std::forward<T>(src));
}
```

ここに T を `std::string&&` としてマクロ的に与えてみる。

```cpp:Tにstring&&を適用してみる
auto foo(std::string&& src) {
  return new std::string&&(std::forward<std::string&&>(src));
}
```

書いていて気持ち悪い。（苦笑）
一体何を要求しているんだ？？（自爆）
そしてコンパイルすると

```
error: cannot allocate reference type 'std::string &&' (aka 'basic_string<char> &&') with new
```

ですよね〜

で、思うに `参照型` って実体（型）のあるモノ（値・インスタンスなど）を参照（キャプチャ）している `状態` を指していて、厳密には `型` ではないとすると、 `new` する際に必要なのは `型` だから `参照` が外れるという流れなら、何となく納得がいく。この辺、 `ポインタ型` と決定的に異なる点なのかな？

となると、<b>「`new` する際に必要なのは `型` だから `参照` が外れる」</b>という根拠を調べないと夜しか眠れない。。。

## 根拠探しの旅が始まる

そもそも `参照型` なる単語があるのかという疑問が出てきたので、ストラウストラップさんの「プログラミング言語C++[第４版]」で目次を調べてみました。
すると「やはり」というか。。。

<b>`参照型` という言葉が無いです。</b>

ちゅうか、 `ポインタ型` も無いですけど。（あれ？）

と、まぁ、気長に探そうって事で色々と調べていたら、[ユニバーサル参照](https://cpprefjp.github.io/lang/cpp11/rvalue_ref_and_move_semantics.html) の記事がありまして。。。
まぁ、この文献は何度も読んではいたのですが、改めてよ〜〜〜〜く読んでみたら、「へぇ〜ユニバーサル参照って正式に転送参照って名前が付いたんだ」と。まぁ、それはいいとして、サンプルコードではこう書いてある。

```cpp
template <typename T> void f(T&& a){}
```

ん？

あっ！

なるほど、そうか！

つまり

```cpp
auto b = bar(std::string("bar"));
```

とすると

```cpp
auto foo(std::string&& src) {
  return new std::string&&(std::forward<std::string&&>(src));
}
```

こうじゃなく

```cpp
auto foo(std::string src) {
  return new std::string(std::forward<std::string>(src));
}
```

こうなる。

ほほぉ〜、じゃぁ、こうすると？

```cpp
auto b = bar<std::string&&>(std::string("bar"));
```
```:コンパイルエラー
error: cannot allocate reference type 'std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > &&' with new 
```

もう、お察しの方も多いと思いますが、そもそも記事の表題が間違っているということでした。（恥）

ということで、元の `set()` 関数が問題という結果になりました。。。
なので、これから修正をしなきゃです。　しょぼぼぼん。。。

## 自己解決

芋づるで色んな修正をしなきゃならなかったけど、かなり納得感が出てコミットすることができたので、もうちょいテストをしてみて運用に回そうと思います。

https://github.com/CODIANZ/cpp-json/commit/c62474029f705cf99e73d98d702e04d991d45ec9

あ〜、これでゆっくり眠れそうだわ。

## あることを思い付く（オチ的なところです）

```cpp
auto b = bar<std::string&&>(std::string("bar"));
```
```:コンパイルエラー
error: cannot allocate reference type 'std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > &&' with new 
```

ある意味、この時点で考えなくてよかったのですが、今になって、これも同じ結果じゃね？と思ったのです。

```cpp
auto b = bar(std::move(std::string("bar"));
```

理由としては、[根拠](https://cpprefjp.github.io/reference/utility/move.html)に記載があるとおり、```std::move()``` は `&&` を付加してムーブセマンティクスを適用すると記載されています。
すなわち、この方法でもコンパイルエラーが出るのじゃないかと。

しかし、エラーが出ません。

で、ここで「テンプレートパラメータの型 `T` が引数に存在し推論する場合 `参照` は推論されないんじゃないか？」と調べてみると、言い回しは異なるけど、こう書いてありました。

「X型の左辺値はX&と導出されて、<b>右辺値はXとして導出される</b>」 [^3]

うん、明確な答えですね。
つまり、 `右辺値参照` で受けるには `T&&` としなければならんのですね。
「お〜明確な根拠を見つけた〜〜〜！」と、声を上げてしまいました。（苦笑）
しかしです、何故この仕様になったのかというのが約１ページ半記載がありまして、これを読み込むのが大変 [^4]。
なので、日を改めて、仕様の背景を苦しみながら楽しませていただきます。

[^3]: ストラウストラップ著・プログラミング言語C++［第4版］「23.5.2.1 参照の導出」より

[^4]: この本は斜め読みができないランキング１位の本です。