C# と VB.NET の質問掲示板

ASP.NET、C++/CLI、Java 何でもどうぞ

C# と VB.NET の入門サイト

Re[7]: コンパイラーは何に基づく、どのタイミングで数値を生成?


(過去ログ 137 を表示中)

[トピック内 9 記事 (1 - 9 表示)]  << 0 >>

■80602 / inTopicNo.1)  コンパイラーは何に基づく、どのタイミングで数値を生成?
  
□投稿者/ ねぶた (1回)-(2016/07/28(Thu) 12:25:51)

分類:[C/C++] 


make_integer_sequence<size_t , 5>( ); // {0,1,2,3,4}という値がコンパイル段階で生成される。
ところで、コンパイラーがどこで何に基づく、どのタイミングで上記数値sequenceを生成するのか、
知りたいです。

ソースコードを追跡してみたら、

make_integer_sequence -----> _Make_seq ------> integral_constant ------> integer_sequence

具体は下記の通り

template<class _Ty,_Ty _Size>
using make_integer_sequence =
typename _Make_seq<_Size < 0, _Size == 0, integral_constant<_Ty, _Size>, integer_sequence<_Ty> >::type;


template<class _Ty, _Ty... _Vals> struct _Make_seq<false, true, .....>

template<class _Ty,_Ty _Val> struct integral_constant

template<class _Ty, _Ty... _Vals> struct integer_sequence

{
static constexpr size_t size() _NOEXCEPT
{
return (sizeof...(_Vals));// get length of parameter list  これだけは run-timeで実行される。
}

};



さて、コンパイラーはどのタイミングで、どういうわけで {0, 1, 2, 3, 4} を生成するのでしょうか。

ぞうぞ宜しくお願いします。



引用返信 編集キー/
■80603 / inTopicNo.2)  Re[1]: コンパイラーは何に基づく、どのタイミングで数値を生成?
□投稿者/ ねぶた (2回)-(2016/07/28(Thu) 12:27:48)
追加情報
開発環境 MS visual studio 2015 C++11,14サポート
引用返信 編集キー/
■80608 / inTopicNo.3)  Re[1]: コンパイラーは何に基づく、どのタイミングで数値を生成?
□投稿者/ とっちゃん (392回)-(2016/07/28(Thu) 13:41:10)
No80602 (ねぶた さん) に返信
>
> さて、コンパイラーはどのタイミングで、どういうわけで {0, 1, 2, 3, 4} を生成するのでしょうか。
>
コンパイル時に、言語仕様に基づいて、生成します。

詳しい仕様は言語仕様を当たってください。

引用返信 編集キー/
■80615 / inTopicNo.4)  Re[2]: コンパイラーは何に基づく、どのタイミングで数値を生成?
□投稿者/ 774RR (437回)-(2016/07/28(Thu) 16:15:18)
{0,1,2,3,4} と書いちゃうとあたかも配列を生成してるように見えるけどそうぢゃなくて
最終的には可変長 template (variadic templates) に展開されているわけです。
可変長 template の説明すると長くなっちゃうけど要は末尾再帰。

最終的には「ソースコード上に式を列挙した」ものになると考えていい。

#include <iostream>
#include <utility>

void g(int x, int y, int z) {
    std::cout << "x=" << x << " y=" << y << " z=" << z << std::endl;
}

template<typename T, T... seq>
void f(std::integer_sequence<T, seq...>) {
    g(seq...);
}

int main() {
    auto x = std::make_integer_sequence<int, 3>();
    f(x);
    std::cout << x.size() << std::endl;
}

上記例題で seq は variadic template なわけですが、この seq がどう扱われるかを簡単に解説すると
g(seq...) の template 展開(実体化)の際に
・先頭要素を1つ展開し、その先頭1個を除去したうえで再帰
・要素が無くなったら展開終了
という約束。

seq には make_integer_sequence によって variadic template として 0 1 2 がコンパイラ内部の作業領域に格納済み
g(seq...) の展開1段階目によって g(0, seq...) になり seq は 1 2 になる
g(seq...) の展開2段階目によって g(0, 1, seq...) になり<中略>
template の展開結果として g(0, 1, 2) なる呼び出しが要求される。
この例では適合する関数があいまいさなく存在するので関数 g() の呼び出しが最終的に生成される

make_integer_sequence<int, 4> だと g(0, 1, 2, 3) なる関数を呼ぼうとするわけで、
引数4つを取る関数 g() があればよし、この例ではないのでエラーってことです。

> コンパイラーはどのタイミングで、どういうわけで
template の実体化タイミングで
variadic template の再帰展開の結果として「式を列挙したかのごとく」振舞う
で、いいかな・・・

引用返信 編集キー/
■80635 / inTopicNo.5)  Re[3]: コンパイラーは何に基づく、どのタイミングで数値を生成?
□投稿者/ ねぶた (3回)-(2016/07/28(Thu) 21:09:21)
No80615 (774RR さん) に返信

774RR 様

いつもお世話になっております。


774RR 先生が書かれました(摘要)

> seq には make_integer_sequence によって variadic template として 0 1 2 がコンパイラ内部の作業領域に格納済み
> g(seq...) の展開1段階目によって g(0, seq...) になり seq は 1 2 になる
> g(seq...) の展開2段階目によって g(0, 1, seq...) になり<中略>
> template の展開結果として g(0, 1, 2) なる呼び出しが要求される。
> この例では適合する関数があいまいさなく存在するので関数 g() の呼び出しが最終的に生成される

これを知りたかったのですね!
本当にありがとうございます。

念のために確認ですけれども、
もちろん、上記作業はrun time ではなく、
コンパイル段階(precompiling 段階かな)で実行し、人間で書いた「g(0, 1, 2)」と同じ文が生成され、
ソースコードのg(seq...)の処で、「g(seq...)」を「g(0, 1, 2)」に置き換えて、
さらにコンパイルしていくわけですよね?


またどうぞ宜しくお願い申し上げます。





引用返信 編集キー/
■80639 / inTopicNo.6)  Re[4]: コンパイラーは何に基づく、どのタイミングで数値を生成?
□投稿者/ 774RR (439回)-(2016/07/28(Thu) 21:35:42)
> コンパイル段階(precompiling 段階かな)で実行し、人間で書いた「g(0, 1, 2)」と同じ文が生成され、
> ソースコードのg(seq...)の処で、「g(seq...)」を「g(0, 1, 2)」に置き換えて、
そうです。

template の展開(実体化)はコンパイラの作業領域の中での(中間)評価
g(seq...) を g(1,2,3) の意味に解釈するのはこの段階
template 実体化後の関数呼び出し(の最適化)は昔ながらのコンパイル時評価(ソースコード確定後の評価)
g(1,2,3) を真に関数呼び出しするかインライン展開するかはこの段階 (C コンパイラでもこういう評価するわけで)

実行時評価をできるかぎりやらない、のが C++ の template (可能な限りコンパイル時評価する)
C# の generics とはその辺が違う
g() の呼び出しがインライン展開できてそのほうがコスト安ならばインライン展開する、
とかそういうあたりをきっちり「コンパイル時評価」してくれるわけです。

variadic template を理解するにあたっては ... がどこに付いたら packing か unpacking か、だけ。
unpack するときは末尾再帰
ってことだけ覚えておけばそこそこ読解できるはず。

引用返信 編集キー/
■80641 / inTopicNo.7)  Re[5]: コンパイラーは何に基づく、どのタイミングで数値を生成?
□投稿者/ ねぶた (4回)-(2016/07/28(Thu) 23:52:07)
さっそくお返答ありがとうございます。

すっきりしました!

ただ、これまでの感覚だと、

「再帰」というのはrun-time段階の話しではないでしょうか[この文の後ろに一つの例を挙げておきます]。
なぜ、コンパイル段階で、g(0, 1, 2)を生成するために、「再帰」実行が必要でしょうか。

コンパイラが g(seq...) に遭遇する前にすでにmake_integer_sequence<int, 3>()を処理済みで 常数列 [0,1,2] がすでに生成され、コンパイラが分かっています。
g関数の定義 g(int x, int y, int z)に関してもコンパイル段階で分かっていますーーー四つの引数。
 
そうすると、unpackingとして、g(seq...)をg(0,1,2) に一発で置き換えれば良いのに、、、と言いたいですが、

やはり、コンパイリング処理的に不可能でしょうか。

=============================

【可変長テンプレート関数を利用した整数値を合計する関数の例------再帰的】

inline int isum()
{
return 0;
}

// 再帰呼び出し isum
template<typename First, typename... Rest>
int isum(const First& first, const Rest&... rest)
{
return first + isum(rest...); // <---これはrun timeで実行されるコード.勿論コンパイル段階で特化し、instanceを生成する。  
}

cout << isum(1, 2, 3, 4) << endl; // 10 <---これはrun timeで実行されるコード  


なので、回帰的な動作はあくまで run-timeで実行され、コンパイル段階ではないではないかと思いますが。


私の考え方が間違ったら、是非ご指摘いただきたいです。

また宜しくお願い申し上げます。


引用返信 編集キー/
■80642 / inTopicNo.8)  Re[6]: コンパイラーは何に基づく、どのタイミングで数値を生成?
□投稿者/ 774RR (440回)-(2016/07/29(Fri) 07:04:06)
コンパイラの実行時に template 実体化ルーチンが再帰している(末尾再帰だからループになっているかも)
ということ。

variadic template 引数を持つ関数 template は、引数を1つ減らしながら [同じ関数] を何度も実体化する
考え方が再帰なだけで、実装はループになってんだと思うわけです。

人間側の理解を助けるためには、この辺のロジックを「再帰」と考えたほうがわかりやすいでしょ?
variadic template な関数を実装するプログラマ、にも
variadic template な関数を利用するプログラマ、にも。


引用返信 編集キー/
■80645 / inTopicNo.9)  Re[7]: コンパイラーは何に基づく、どのタイミングで数値を生成?
□投稿者/ ねぶた (5回)-(2016/07/29(Fri) 10:52:29)
No80642 (774RR さん) に返信
> コンパイラの実行時に template 実体化ルーチンが再帰している(末尾再帰だからループになっているかも)
> ということ。
>
> variadic template 引数を持つ関数 template は、引数を1つ減らしながら [同じ関数] を何度も実体化する
> 考え方が再帰なだけで、実装はループになってんだと思うわけです。
>
> 人間側の理解を助けるためには、この辺のロジックを「再帰」と考えたほうがわかりやすいでしょ?
> variadic template な関数を実装するプログラマ、にも
> variadic template な関数を利用するプログラマ、にも。
>

そうですか。
この辺はrun-timeの話ではなくて、コンパイル過程においてのコンパイラの動きですね。
少々混乱しました。

丁寧なご解説どうもありがとうございました。


解決済み
引用返信 編集キー/


トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

管理者用

- Child Tree -