C++CLI: template 型引数にマネージ型を渡す †
C++/CLIでは当然ながら template を記述できますが、その型引数にはマネージ型も渡すことができます。
C++11の Variadic templates 機能をサポートしているVC++2013では、次のようなコードも書くことができます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
-
-
!
|
-
|
!
|
-
!
|
-
|
!
|
-
!
|
-
|
!
!
-
-
!
|
-
!
|
|
-
!
|
|
|
|
!
| using namespace System;
namespace
{
template<class T, class ... TArgs>
T^ Create(TArgs ... args)
{
return gcnew T(args...);
}
template<class T, class U>
auto Add(T a, U b) -> decltype(a + b)
{
return (a + b);
}
template<class T, class U, class ... TArgs>
auto Add(T a, U b, TArgs ... args) -> decltype(Add(a + b, args...))
{
return Add(a + b, args...);
}
}
int main(cli::array<String^>^ args)
{
auto test = gcnew cli::array<wchar_t>{ L't', L'e', L's', L't' };
auto a = Create<String>(test);
auto b = Create<String>(L'!', 3);
auto str = Add(a, b, L' ', DateTime::Now);
Console::WriteLine(str);
return 0;
}
|
C++的にはそれほど驚きの無いコードですが、C#のジェネリック(generic)では実現できない次のような処理をあっさりと書けてしまっています。
- 引数付きコンストラクタ呼び出しによるオブジェクト生成。
- template 型同士の演算。
しかも template であるため関数の実体化はコンパイル時に行われ、もし引数を間違えていてもコンパイルエラーによって気付くことができます。
上述のコードをC#で実現しようとすると次のようなコードになるでしょう。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
|
-
|
-
-
|
|
!
-
|
!
|
-
|
|
!
-
|
|
-
|
!
|
!
|
-
|
|
!
-
|
|
|
|
|
|
|
|
|
|
!
!
!
| using System;
namespace Sample
{
static class Program
{
static T Create<T>(params object[] args)
{
return (T)Activator.CreateInstance(typeof(T), args);
}
static dynamic Add(dynamic a, dynamic b, params dynamic[] args)
{
dynamic r = a + b;
foreach (var arg in args)
{
r += arg;
}
return r;
}
static int Main(string[] args)
{
var test = new[] { 't', 'e', 's', 't' };
var a = Create<string>(test);
var b = Create<string>('!', 3);
var str = Add(a, b, ' ', DateTime.Now);
Console.WriteLine(str);
return 0;
}
}
}
|
下記の代替手段を利用しています。
- ジェネリック型に対して引数付きコンストラクタを呼び出すことはできないため、
Activator.CreateInstance
メソッドを利用。
- ジェネリック型同士の演算は無理、かつ戻り値の型を推定できないため、
dynamic
を利用。
「代替手段があるならいいんじゃないの?」と思うかもしれませんが、これらの手段ではもし引数に渡す値が間違っていてもコンパイルが通ってしまい、実行時例外が起きるまで間違いに気付くことができません。
安全性の面で言えば、コンパイル時にミスを検出できるC++/CLI側のコードに軍配が上がるでしょう。
C++/CLIというと「C++の資産(ライブラリ)をラップするための言語」という認識の人が多いですし、それも間違いではないのですが、「マネージ型主体のコードを書きつつピンポイントで template 等のC++機能を利用する」という使い方をしてみてもいいかもしれません。