none
Visual Basic 2008 のジェネリックにおける型推論の仕様について RRS feed

  • 質問

  • ネットの皆さん こんにちは

     Visual Basic 2008 でジェネリックを使用していて、型推論の仕様に疑問を持ちました。

     最初にやったのは、互いに同じ型でバインドされた2つのインターフェースをパラメータとして取るメソッドでした。 

     Public Function Foo(Of T)(arg1 As Interface1(Of T), _
                                             arg2 As Interface2(Of T)) As T
         :
         :
     End Function
    
    
    



     ここまでは問題ありません。呼び出し元もそれなりにすっきり書けます。

         Dim obj1 As New Implementation1(Of Integer)
         Dim obj2 As New Implementation2(Of Integer)
         Dim ret As Integer = 0
         ret = Foo(Of Integer)(obj1, obj2)
    
    



     次に、パラメータとしてインターフェースを渡すのではなく、それらの「インターフェースを実装している」という制約の元に任意の型を渡すようにしたいと考えました。最初にやって(当然)失敗したのは以下のような記述です。

     Public Function Foo(Of ImpT1 As Interface1(Of T), _
                                        ImpT2 As Interface2(Of T)) _
                                   (arg1 As ImpT1, arg2 As ImpT2) As T
         :
         :
     End Function
    
    
    

     これは T の宣言がないことを理由にコンパイルエラーになります。それでは、と試して以下のようにしたところ、コンパイルは通りました。

     Public Function Foo(Of T, ImpT1 As Interface1(Of T), _
                               ImpT2 As Interface2(Of T)) _
                        (arg1 As ImpT1, arg2 As ImpT2) As T
         :
         :
     End Function
    
    


     一件落着、と思ったのですが、今度は以下の呼び出しがコンパイルエラーになってしまいました。

         Dim obj1 As New Implementation1(Of Integer)
         Dim obj2 As New Implementation2(Of Integer)
         Dim ret As Integer = 0
         ret = Foo(obj1, obj2)         '// ←  ERROR : T を推論できない
    
    

     コンパイルエラーのメッセージは、「パラメータ T を推論できません」というものです。結果、以下のように型を全て明示して記述しなければならなくなりました。

         Dim obj1 As New Implementation1(Of Integer)
         Dim obj2 As New Implementation2(Of Integer)
         Dim ret As Integer = 0
         ret = Foo(Of Integer, Implementation1(Of Integer), _
                               Implementation2(Of Integer))(obj1, obj2)
    
    

     直接的な原因は、パラメータに T そのものの値が含まれていないからです。Foo に v As T などといったパラメータを追加すれば、Foo の呼び出しにおいて型名を明示的に指定しなくても呼び出せるようになります。

     そこで質問ですが、Visual Basic のジェネリックにおける型推論機構は、例えば上記において Implementation1(Of Integer) が Interface1(Of Integer) を実装していることを理由に、Foo の(パラメータからは決定できない)T が Integer であることを推論してくれないものなのでしょうか? してくれない場合、どうにかして上記のような口数の多い呼び出しの記述を簡潔にする方法はないものでしょうか?


     まだ .NET 以降の Visual Basic に慣れていないため、変な勘違いをしているかもしれません。あわせて御指摘頂ければ幸いです。

     以上、よろしくお願いします。

     

    2011年9月2日 3:22

回答

  • 長い文面は正直読めてません。

    私のジェネリックに対する理解は、C++ の template によるバイアスがかなり強くかかっています。そのせいで Visual Basic のジェネリックを正しく理解できていないものと思われます。

    最も大きいのは、C++ の template はコンパイル時に置き換えたコードが生成されるのに対して、.NET の Generics は実行時まで型が決まらないことではないでしょうか。
    従って、コンパイル時に解決できないことをやろうとする時点で破綻します。
    (STL/CLR は template が使われていますね)

    .NET Generics はコンパイル時点では制約とそれによってできることのみチェックがされます。
    従って、制約で明言されていないメソッド、プロパティを参照することはできません。

    # 型推論の優先順位とかはぱっと出せるほど理解していないので触れるのは避けます。
    # 検索すればそういった投稿、記事が出てくるとは思います。


    質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。
    2011年9月2日 15:52
    モデレータ
  • 次のページが参考になると思います。

    (参考)
    C++ テンプレートと C# ジェネリックの違い (C# プログラミング ガイド)
    http://msdn.microsoft.com/ja-jp/library/c6cyy67b(v=VS.90).aspx

    C#向けのページですが、事情はVB.NETでも同じだと思います。

    要点としては、ジェネリックでは型パラメータの実際の型で処理を振り分けることができません。
    理由は、Azulean様が指摘されている通りです。

    (挙げられている例でいうと、SortメソッドでのIteratorT型パラメータ(実際の型ではない)がRandomAccessIterator(Of T)でないため、常に1番目のSortImpメソッドが呼ばれるのだと思います。)

    リフレクションを使えばできるかもしれませんが、型で振り分けるとジェネリックを使う意味がなくなる気がします。


    • 編集済み Alfred360 2011年9月2日 23:05 誤記訂正
    • 回答としてマーク iksm 2011年9月3日 15:24
    2011年9月2日 22:39
  • しかし、それでも常に BidirectionalIteratorTag を受け取る方の SortImp に解決される理由は私の中で説明が付きません。

    Sort メソッドは "IteratorT As BidirectionalIterator(Of T)" という制約しかないので、その条件下で解決できる SortImp メソッドを呼ぶようにコンパイルされるだけです。
    コンパイル時点では "RandomAccessIterator(Of T)" という厳しい方の制約は呼び出されないメソッドになるだけでしょう。

    これは、Generics を使うメソッド・クラスを DLL の外に公開できる以上、このメソッド・クラスをコンパイルする時点で呼び分けることはできません。
    呼び分ける実装にコンパイルする(実行時にダウンキャストする)という選択肢も考えられたかもしれませんが、そういった仕組みは採用されていませんので、コンパイル時にどのメソッドを呼ぶかは決定できなければなりません。

    たとえば:

     

    Public Sub Sort(Of T, IteratorT As BidirectionalIterator(Of T)) _
    		(ByVal itr1 As IteratorT, ByVal itr2 As IteratorT)
    	Dim tag As IteratorT = itr1
    	Dim tag2 As RandomAccessIterator(Of T) = _
    		TryCast(itr1, RandomAccessIterator(Of T))
    	If tag2 Is Nothing Then
    		Call SortImp(Of T, IteratorT)(itr1, itr2, tag)
    	Else
    		Call SortImp(Of T, RandomAccessIterator(Of T))(itr1, itr2, tag2)
    	End If
    End Sub
    

    何となくこんな感じを示すものであって、コンパイル可能かどうか、最適かどうかは調べていません。
    もっとも、実行時にダウンキャストのチェックが入るので、実行効率は落ちますよ。

     

    非 Nothing を渡した場合のみキャストでエラーになるところを見ると、ジェネリックといっても全てコンパイル時に解決しているわけではなさそうなので、狙っていることはできなさそうな感触です。

    Generics のメソッドを呼び出したコードをコンパイルする際には制約を満たすかどうかがチェックされます。
    Nothing 以外の場合はコンパイル時に何らかの型情報があるわけですから、制約である "BidirectionalIterator(Of T)" を満たすかどうかをチェックします。

    ただ、満たすかどうかをチェックするだけで、C++ template のようにコンパイルしてできあがった DLL/EXE で実際の型に置き換わるわけではありません。Generics として残った形で DLL/EXE になります。コンパイル時点で呼び出されていなかった Generics メソッドも DLL/EXE で public として公開することができ、それを利用(参照)した DLL/EXE のコンパイル時に制約をチェックします。
    (.NET Framework の List(Of T) などは DLL 形式で公開されている)

    C++ template はコンパイル時に完全に解決して(置き換えて)しまうので、それを使った関数を template のまま、DLL 形式で公開できませんよね?ヘッダーファイルというソースコードレベルで共有することになります。
    (vector というファイルで template のヘッダーがあるように、コンパイル時点で template はソースコードとして参照できなければならない)


    質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。
    2011年9月2日 23:31
    モデレータ

すべての返信

  • > Public Function Foo(Of T)(arg1 As Interface1(Of T), _
    >                                          arg2 As Interface2(Of T)) As T
    > End Function
    >  ここまでは問題ありません。呼び出し元もそれなりにすっきり書けます。
    >       Dim obj1 As New Implementation1(Of Integer)
    >      Dim obj2 As New Implementation2(Of Integer)
    >      Dim ret As Integer = 0
    >      ret = Foo(Of Integer)(obj1, obj2)

    この時点でも次のように推論させることができますね。
    ret = Foo(obj1, obj2)

    > 次に、パラメータとしてインターフェースを渡すのではなく、それらの「インターフェースを実装している」という制約の元に任意の型を渡すようにしたいと考えました。

    "Interface1(Of T)" などはインターフェイスですよね?(文末のコードを想定)
    でしたらすでに上記の Foo の定義で実現できていると思います。
    型パラメータによる制約ではなく、引数の型による制限によるものですけど。

    > そこで質問ですが、Visual Basic のジェネリックにおける型推論機構は、例えば上記において Implementation1(Of Integer) が Interface1(Of Integer) を実装していることを理由に、Foo の(パラメータからは決定できない)T が Integer であることを推論してくれないものなのでしょうか?

    これは Visual Basic に限らず、戻り値のみに現れる型パラメータを推論させることはできないので、仕方ないと思いますが、そもそも定義が冗長だと思います。

    なお、コード全体としては以下のような状況を想定しています。

    Public Class Form1
        Private Sub Button1_Click(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles Button1.Click
            Dim obj1 As New Implementation1(Of Integer)
            Dim obj2 As New Implementation2(Of Integer)
            Foo(obj1, obj2)
        End Sub

        Public Function Foo(Of T)(ByVal arg1 As Interface1(Of T), ByVal arg2 As Interface2(Of T)) As T
        End Function
    End Class

    Public Interface Interface1(Of T)
    End Interface
    Public Interface Interface2(Of T)
    End Interface
    Public Class Implementation1(Of T)
        Implements Interface1(Of T)
    End Class
    Public Class Implementation2(Of T)
        Implements Interface2(Of T)
    End Class

    2011年9月2日 7:31
  • これは Visual Basic に限らず、戻り値のみに現れる型パラメータを推論させることはできないので、仕方ないと思いますが、そもそも定義が冗長だと思います。

    TH01さんと同意見です。もうご覧になっているとは思いますが、一応参考になるページをご紹介しておきます。

    CA1004: ジェネリック メソッドは型パラメーターを指定しなければなりません
    http://msdn.microsoft.com/ja-jp/library/ms182150(v=VS.100).aspx

    また、以下のようにFooを別クラスに定義し、クラスに型パラメータを与えれば一応は実現できます。以下のページはC#ですが、VBでも同様です。

    C#のジェネリックメソッドの型推論
    http://gushwell.ldblog.jp/archives/50730144.html

     


    ★良い回答には回答済みマークを付けよう! わんくま同盟 MVP - Visual C# http://d.hatena.ne.jp/trapemiya/
    2011年9月2日 8:38
    モデレータ
  • 皆さん、ありがとうございます。

     最初の投稿が長くなりすぎたので本来の意図を書ききることができず、上手くお伝えできなかったようです。申し訳ありません。ここでは、パラメータとして渡しているインターフェースを実装(または派生抽象クラスから派生)しているクラスの「実際の型」を渡したいと考えています。

     余談ぽくなりますが有体に申しますと、C++ の STL を VB.NET のクラスライブラリに移植するという作業をやっています。Visual Basic 6.0 時代には Variant 型とインターフェースだけで実装したのですが、最近の Visual Basic にはジェネリックの機能があると知り、それを利用した実装ができないか検討しているところです。

     たとえば双方向反復子またはランダムアクセス反復子のいずれかを許容し,ランダムアクセス反復子の場合にはその機能を利用して高速に動作させるアルゴリズムを組む、というのは STL にもあります。やりたかったのは、STL の iterator_tag のような方法でコンパイル時ポリモーフィズムを実現する、ということでした。

     ざっくりですが、イメージを以下に書いてみます。Algorithm.Sort に抽象クラス BidirectionalIterator(Of T) ではなく、実際の反復子の型を渡すことにより、それが BidirectionalIteratorTag と RandomAccessIteratorTag のどちらを持っているかによって振り分けができるだろう、というのがそもそもの要件でした。実際には、以下のコードには他にも問題点があるようなので、根本から練り直す必要がありそうです。

    Public Interface BidirectionalIteratorTag
    End Interface
    
    Public Interface RandomAccessIteratorTag
    End Interface
    
    Public MustInherit Class BidirectionalIterator(Of T)
        '  :
        '  :
    End Class
    
    Public MustInherit Class RandomAccessIterator(Of T)
        Inherits BidirectionalIterator(Of T)
        '  :
        '  :
    End Class
    
    Public Class ListIterator(Of T)
        Inherits BidirectionalIterator(Of T)
        Implements BidirectionalIteratorTag
        '  :
        '  :
    End Class
    
    Public Class VectorIterator(Of T)
        Inherits RandomAccessIterator(Of T)
        Implements RandomAccessIteratorTag
        '  :
        '  :
    End Class
    
    Public Module Algorithm
    
        Public Sub Sort(Of T, IteratorT As BidirectionalIterator(Of T)) _
                       (ByVal itr1 As IteratorT, ByVal itr2 As IteratorT)
            Call SortImp(Of T, IteratorT)(itr1, itr2)
        End Sub
    
        Public Sub SortImp(Of T, IterT As {BidirectionalIteratorTag,  _
                                           BidirectionalIterator(Of T)}) _
                          (ByVal itr1 As IterT, ByVal itr2 As IterT)
        End Sub
    
        Public Sub SortImp(Of T, IterT As {RandomAccessIteratorTag,  _
                                           RandomAccessIterator(Of T)}) _
                          (ByVal itr1 As IterT, ByVal itr2 As IterT)
        End Sub
    
    End Module
    
    
    

     

    2011年9月2日 10:37
  • 恐縮ですが、連投失礼します。

     やろうと思っていたイメージでコンパイルが通るところまで行きました。まず、iterator_tag に該当する空のインターフェースを作成しておきます。とりあえずの必要最低限ということで、双方向とランダムアクセスの2つです。

    Public Interface BidirectionalIteratorTag
    End Interface
    
    Public Interface RandomAccessIteratorTag
    End Interface
    
    

     次に、各種の反復子を抽象化するインターフェース/クラスを作成します。上記の iterator_tag 代替インターフェースは実装クラスで拾うのでここでは Implements しません。

    Public Interface InputIterator(Of T)
    End Interface
    
    Public Interface OutputIterator(Of T)
    End Interface
    
    Public MustInherit Class ForwardIterator(Of T)
        Implements InputIterator(Of T)
        Implements OutputIterator(Of T)
    End Class
    
    Public MustInherit Class BidirectionalIterator(Of T)
        Inherits ForwardIterator(Of T)
    End Class
    
    Public MustInherit Class RandomAccessIterator(Of T)
        Inherits BidirectionalIterator(Of T)
    End Class
    
    

     そして、試しにリストとベクタの反復子を作成してみます。それぞれ、反復子の抽象クラスと iterator_tag 代替インターフェースを継承/実装します。

    Public NotInheritable Class ListIterator(Of T)
        Inherits BidirectionalIterator(Of T)
        Implements BidirectionalIteratorTag
    End Class
    
    Public NotInheritable Class VectorIterator(Of T)
        Inherits RandomAccessIterator(Of T)
        Implements RandomAccessIteratorTag
    End Class
    
    
    

     これらを利用する Algorithm モジュールをテスト的に作成します。BidirectionalIterator(Of T) からの派生という制約を受ける任意の型 IteratorT によるシーケンスをソートするメソッド Sort より、iterator_tag 代替インターフェースを利用して2種類にオーバーロードされた SortImp に振り分けようとしています。

    Module Algorithm
    
        Public Sub Sort(Of T, IteratorT As BidirectionalIterator(Of T)) _
                       (ByVal itr1 As IteratorT, ByVal itr2 As IteratorT)
            Dim tag As IteratorT = itr1
            Call SortImp(Of T, IteratorT)(itr1, itr2, tag)
        End Sub
    
        Private Sub SortImp(Of T, IteratorT As BidirectionalIterator(Of T)) _
                           (ByVal itr1 As IteratorT, _
                            ByVal itr2 As IteratorT, ByVal tag As BidirectionalIteratorTag)
            MsgBox("SortImp For BidirectionalIterator")
        End Sub
    
        Private Sub SortImp(Of T, IteratorT As RandomAccessIterator(Of T)) _
                           (ByVal itr1 As IteratorT, _
                            ByVal itr2 As IteratorT, ByVal tag As RandomAccessIteratorTag)
            MsgBox("SortImp For RandomAccessIterator")
        End Sub
    
    End Module
    
    

     これで、以下のようにすれば RandomAccessIterator 版の SortImp に(コンパイル時解決で)到達するものと考えました。

    Module Module1
    
        Sub Main()
            Dim itr1 As VectorIterator(Of Integer) = Nothing
            Dim itr2 As VectorIterator(Of Integer) = Nothing
            Call Algorithm.Sort(Of Integer, VectorIterator(Of Integer))(itr1, itr2)
        End Sub
    
    End Module
    
    

     ‥‥‥しかし、これはコンパイルには通るものの、思ったようには動いてくれません。何故か BidirectionalIterator 版の SortImp に到達してしまいます。

     次に、Main メソッドを以下のように変えてみました。

    Module Module1
    
        Sub Main()
            Dim itr1 As New VectorIterator(Of Integer)
            Dim itr2 As New VectorIterator(Of Integer)
            Call Algorithm.Sort(Of Integer, VectorIterator(Of Integer))(itr1, itr2)
        End Sub
    
    End Module
    
    

     すると、今度は Algorithm.Sort から SortImp をコールしようとした時点で実行時エラーが発生してしまいました。「VectorIterator を BidirectionalIteratorTag に変換できない」というのです。

     この時点で混乱してきました。ここで Algorithm.Sort の呼出しにおける IteratorT は VectorIterator(Of Integer) のはずで、VectorIterator(Of Integer) は RandomAccessIteratorTag および RandomAccessIterator(Of Integer) のみから派生しているはずです。であれば、SortImp の tag パラメータは RandomAccessIteratorTag しか選択肢がないはず。それなのに、なぜ SortImp の呼び出しは BidirectionalIterator 版 SortImp に解決されるのでしょう?

     ここまでお読みくださった方であればおわかりかと思いますが、私のジェネリックに対する理解は、C++ の template によるバイアスがかなり強くかかっています。そのせいで Visual Basic のジェネリックを正しく理解できていないものと思われます。そのあたりの正しい理解の助けとなる情報のポインタだけでも示していただけると幸いです。よろしくお願いします。

     

    2011年9月2日 13:31
  • 長い文面は正直読めてません。

    私のジェネリックに対する理解は、C++ の template によるバイアスがかなり強くかかっています。そのせいで Visual Basic のジェネリックを正しく理解できていないものと思われます。

    最も大きいのは、C++ の template はコンパイル時に置き換えたコードが生成されるのに対して、.NET の Generics は実行時まで型が決まらないことではないでしょうか。
    従って、コンパイル時に解決できないことをやろうとする時点で破綻します。
    (STL/CLR は template が使われていますね)

    .NET Generics はコンパイル時点では制約とそれによってできることのみチェックがされます。
    従って、制約で明言されていないメソッド、プロパティを参照することはできません。

    # 型推論の優先順位とかはぱっと出せるほど理解していないので触れるのは避けます。
    # 検索すればそういった投稿、記事が出てくるとは思います。


    質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。
    2011年9月2日 15:52
    モデレータ
  • .NET Generics はコンパイル時点では制約とそれによってできることのみチェックがされます。
    従って、制約で明言されていないメソッド、プロパティを参照することはできません。

    ありがとうございます。

     なんとなくわかってきたような気がします。制約は複数の基底クラスやインターフェースからの派生を義務付ける時に有用なものであって、単一の基底を指定するだけの場合、ジェネリックにしないでその基底クラス/インターフェースを受け取ることとほとんど変わらない、ということでしょうか?

     とはいえ、前回の私のコードがコンパイルに通るところを見ると、完全に同じではないようですね。しかし、それでも常に BidirectionalIteratorTag を受け取る方の SortImp に解決される理由は私の中で説明が付きません。非 Nothing を渡した場合のみキャストでエラーになるところを見ると、ジェネリックといっても全てコンパイル時に解決しているわけではなさそうなので、狙っていることはできなさそうな感触です。

     

    2011年9月2日 21:49
  • 次のページが参考になると思います。

    (参考)
    C++ テンプレートと C# ジェネリックの違い (C# プログラミング ガイド)
    http://msdn.microsoft.com/ja-jp/library/c6cyy67b(v=VS.90).aspx

    C#向けのページですが、事情はVB.NETでも同じだと思います。

    要点としては、ジェネリックでは型パラメータの実際の型で処理を振り分けることができません。
    理由は、Azulean様が指摘されている通りです。

    (挙げられている例でいうと、SortメソッドでのIteratorT型パラメータ(実際の型ではない)がRandomAccessIterator(Of T)でないため、常に1番目のSortImpメソッドが呼ばれるのだと思います。)

    リフレクションを使えばできるかもしれませんが、型で振り分けるとジェネリックを使う意味がなくなる気がします。


    • 編集済み Alfred360 2011年9月2日 23:05 誤記訂正
    • 回答としてマーク iksm 2011年9月3日 15:24
    2011年9月2日 22:39
  • しかし、それでも常に BidirectionalIteratorTag を受け取る方の SortImp に解決される理由は私の中で説明が付きません。

    Sort メソッドは "IteratorT As BidirectionalIterator(Of T)" という制約しかないので、その条件下で解決できる SortImp メソッドを呼ぶようにコンパイルされるだけです。
    コンパイル時点では "RandomAccessIterator(Of T)" という厳しい方の制約は呼び出されないメソッドになるだけでしょう。

    これは、Generics を使うメソッド・クラスを DLL の外に公開できる以上、このメソッド・クラスをコンパイルする時点で呼び分けることはできません。
    呼び分ける実装にコンパイルする(実行時にダウンキャストする)という選択肢も考えられたかもしれませんが、そういった仕組みは採用されていませんので、コンパイル時にどのメソッドを呼ぶかは決定できなければなりません。

    たとえば:

     

    Public Sub Sort(Of T, IteratorT As BidirectionalIterator(Of T)) _
    		(ByVal itr1 As IteratorT, ByVal itr2 As IteratorT)
    	Dim tag As IteratorT = itr1
    	Dim tag2 As RandomAccessIterator(Of T) = _
    		TryCast(itr1, RandomAccessIterator(Of T))
    	If tag2 Is Nothing Then
    		Call SortImp(Of T, IteratorT)(itr1, itr2, tag)
    	Else
    		Call SortImp(Of T, RandomAccessIterator(Of T))(itr1, itr2, tag2)
    	End If
    End Sub
    

    何となくこんな感じを示すものであって、コンパイル可能かどうか、最適かどうかは調べていません。
    もっとも、実行時にダウンキャストのチェックが入るので、実行効率は落ちますよ。

     

    非 Nothing を渡した場合のみキャストでエラーになるところを見ると、ジェネリックといっても全てコンパイル時に解決しているわけではなさそうなので、狙っていることはできなさそうな感触です。

    Generics のメソッドを呼び出したコードをコンパイルする際には制約を満たすかどうかがチェックされます。
    Nothing 以外の場合はコンパイル時に何らかの型情報があるわけですから、制約である "BidirectionalIterator(Of T)" を満たすかどうかをチェックします。

    ただ、満たすかどうかをチェックするだけで、C++ template のようにコンパイルしてできあがった DLL/EXE で実際の型に置き換わるわけではありません。Generics として残った形で DLL/EXE になります。コンパイル時点で呼び出されていなかった Generics メソッドも DLL/EXE で public として公開することができ、それを利用(参照)した DLL/EXE のコンパイル時に制約をチェックします。
    (.NET Framework の List(Of T) などは DLL 形式で公開されている)

    C++ template はコンパイル時に完全に解決して(置き換えて)しまうので、それを使った関数を template のまま、DLL 形式で公開できませんよね?ヘッダーファイルというソースコードレベルで共有することになります。
    (vector というファイルで template のヘッダーがあるように、コンパイル時点で template はソースコードとして参照できなければならない)


    質問スレッドで解決した場合は、解決の参考になった投稿に対して「回答としてマーク」のボタンを押すことで、同じ問題に遭遇した別のユーザが役立つ投稿を見つけやすくなります。
    2011年9月2日 23:31
    モデレータ
  • 皆さん、こんばんは。そしてありがとうございます。

     ご教示頂いた情報で、ひとまず納得できたように思います。今後は無理にやり方まで模倣せず、(そして結果として実行時効率を多少犠牲にすることになるかもしれませんが、)クライアントコード側での使い易さを優先してデザインしてみます。


    ありがとうございました。

     

     

    2011年9月3日 15:28