ChatGPT に decltype の使い方を教えてもらった件

### 希望

vector<あるタイプ> とその要素に適用する関数やラムダ式を与えたら、
vector<別のタイプ> を返す関数があったら便利だと思いませんか？

下の Apply がこの記事の成果物なんですが、こんなの C++ の仕様を隅々まで知っていてもなかなか思いつかないですよね。

どうやってこの関数に辿り着いたかというと、ChatGPT にヒントを教えてもらいました。すごいなChatGPT。

```
#include <iostream>
using namespace std;

template < typename T, typename F > auto
Apply( const vector< T >& _, F f ) {
	vector< decltype( f( *_.begin() ) ) > $;
	for ( auto& _: _ ) $.emplace_back( f( _ ) );
	return $;
}
int
main( int argc, char* argv[] ) {
	{	//	int to double
		vector< int >	_ = { 1, 2, 3 };
		auto coef = 1.5;
		auto $ = Apply( _, [ & ]( int _ ){ return _ * coef; } );
		for ( auto& _: $ ) cout << '\t' << _;
		cout << endl;
	}
	{	//	double to string
		vector< double >	_ = { 1.5, 2.5, 3.5 };
		auto quote = '\"';
		auto $ = Apply( _, [ & ]( double _ ){ return quote + to_string( _ ) + quote; } );
		for ( auto& _: $ ) cout << '\t' << _;
		cout << endl;
	}
	{	//	引数の配列が空
		vector< double >	_;
		auto quote = '\"';
		auto $ = Apply( _, [ & ]( double _ ){ return quote + to_string( _ ) + quote; } );
		for ( auto& _: $ ) cout << '\t' << _;
		cout << endl;
	}
}
----	実行結果
	1.5	3	4.5
	"1.500000"	"2.500000"	"3.500000"

```

順を追って考えてみます。

### CASE 1: 関数ポインタ

例えば int の配列の全ての要素に対して、
int を引数にとって文字列を返す関数を適用して、
文字列の配列に変換したい。

```
#include <iostream>
using namespace std;

vector< string >
Apply( const vector< int > _, string(*f)( int ) ) {
    vector< string >    $;  
    for ( auto& _: _ ) $.emplace_back( f( _ ) );
    return $;
}
int
main( int argc, char* argv[] ) {
    vector< int >   _ = { 1, 2, 3 };
    auto $ = Apply( _, []( int _ ){ return '\"' + to_string( _ ) + '\"'; } );
    for ( auto& _: $ ) cout << _ << endl;
}
----	実行結果
"1"
"2"
"3"
```

C の時代からある、関数ポインタを引数として渡す書き方です。キャプチャなしのラムダは関数と同じ扱いでいけます。



### CASE 2: キャプチャー付きのラムダ

CASE1 の f のところに関数ではなくキャプチャー付きのラムダを渡したい。

CASE1 の Apply をそのまま使うと、`error: no matching function for call to 'Apply'`と言われます。

これを解決する一番簡単な方法は、テンプレートを使ってキャプチャー付きのラムダを展開する方法です。

以下のようにします。

```
#include <iostream>
using namespace std;

template< typename F >  vector< string >
Apply( const vector< int > _, F f ) {
    vector< string >    $;  
    for ( auto& _: _ ) $.emplace_back( f( _ ) );
    return $;
}
int
main( int argc, char* argv[] ) {
    vector< int >   _ = { 1, 2, 3 };
    auto quote = '\"';
    auto $ = Apply( _, [ & ]( int _ ){ return quote + to_string( _ ) + quote; } );
    for ( auto& _: $ ) cout << _ << endl;
}
----	実行結果
"1"
"2"
"3"
```

のが一番簡単な方法です。

テンプレートを使わずに以下のようにしてもいけます。

```
#include <iostream>
using namespace std;

vector< string >
Apply( const vector< int > _, function< string( int ) > f ) {
    vector< string >    $;
    for ( auto& _: _ ) $.emplace_back( f( _ ) );
    return $;
}
int
main( int argc, char* argv[] ) {
    vector< int >   _ = { 1, 2, 3 };
    auto quote = '\"';
    auto $ = Apply( _, [ & ]( int _ ){ return quote + to_string( _ ) + quote; } );
    for ( auto& _: $ ) cout << _ << endl;
}
----	実行結果
"1"
"2"
"3"
```

### CASE 3: 引数の一部をテンプレートに

CASE 2: の引数に int 以外の型を渡したい。

```
#include <iostream>
using namespace std;

template< typename T, typename F >  vector< string >
Apply( const vector< T > _, F f ) { 
    vector< string >    $;  
    for ( auto& _: _ ) $.emplace_back( f( _ ) );
    return $;
}
int
main( int argc, char* argv[] ) {
    vector< double >   _ = { 1, 2, 3 };
    auto quote = '\"';
    auto $ = Apply( _, [ & ]( int _ ){ return quote + to_string( _ ) + quote; } );
    for ( auto& _: $ ) cout << _ << endl;
}
----	実行結果
"1"
"2"
"3"
```

function は残念ながら template 引数の T を受け取ってくれないようです。
```
#include <iostream>
using namespace std;

template< typename T, typename F >  vector< string >
Apply( const vector< T > _, function< string( T ) > f ) { 
    vector< string >    $;  
    for ( auto& _: _ ) $.emplace_back( f( _ ) );
    return $;
}
int
main( int argc, char* argv[] ) {
    vector< double >   _ = { 1, 2, 3 };
    auto quote = '\"';
    auto $ = Apply( _, [ & ]( int _ ){ return quote + to_string( _ ) + quote; } );
    for ( auto& _: $ ) cout << _ << endl;
}
----	コンパイル結果
3-2.cpp:14:14: error: no matching function for call to 'Apply'
    auto $ = Apply( _, [ & ]( int _ ){ return quote + to_string( _ ) + quote; } );
             ^~~~~
3-2.cpp:5:1: note: candidate template ignored: could not match 'function<std::string (T)>' (aka 'function<basic_string<char, char_traits<char>, allocator<char>> (T)>') against '(lambda at 3-2.cpp:14:24)'
Apply( const vector< T > _, function< string( T ) > f ) { 
^
```

### CASE 4: 本題

ここからが本題です。以下のようにやればいけそうにも思えますが、実際はエラーになります。

```
#include <iostream>
using namespace std;


template< typename I, typename O, typename F >  vector< O > 
Apply( const vector< I > _, F f ) { 
    vector< O > $;  
    for ( auto& _: _ ) $.emplace_back( f( _ ) );
    return $;
}
int
main( int argc, char* argv[] ) { 
    vector< double >   _ = { 1, 2, 3 };
    auto quote = '\"';
    auto $ = Apply( _, [ & ]( int _ ){ return quote + to_string( _ ) + quote; } );
    for ( auto& _: $ ) cout << _ << endl;
}
----	コンパイル結果
4.cpp:15:14: error: no matching function for call to 'Apply'
    auto $ = Apply( _, [ & ]( int _ ){ return quote + to_string( _ ) + quote; } );
             ^~~~~
4.cpp:6:1: note: candidate template ignored: couldn't infer template argument 'O'
Apply( const vector< I > _, F f ) {
^
```

テンプレート引数の O が推測できない、と言われています。途方に暮れますね。昔ならここで C++ の仕様をネットで長時間かけて調べたところです。もうそんな気力はないので、ChatGPT に聞いてみます。投げた質問を３つ順に載せておきます。

```
c++ でjavascript の　map にあたる関数を作成することはできますか？
Javascript のように新たに生成されたvectorを戻り値として受け取るようにはできませんか？
result_of は削除されています。
```

この質問で生成された答えをちょっとブラッシュアップしたのが、冒頭のものです。
興味がある方はトレースしてくださいね！