C++CLI:同名のプロパティやメソッドを持つインタフェースの明示的実装 †
- ネタ元
- c++ cli - C++/CLI: Implementing IList and IList<T> (explicit implementation of a default indexer) - Stack Overflow
例えば独自のリストクラスを作る際に IList<T>
インタフェースと IList
インタフェースを両方実装したい場合があります。
実装方法は色々あると思いますが、 IList<T>
を普通に実装し、 IList
は明示的実装をする方法が割と一般的かと思います。
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
|
-
|
-
-
!
-
|
|
!
|
-
!
|
-
!
-
|
|
!
|
-
!
-
|
!
|
-
!
!
| using System;
using System.Collections;
using System.Collections.Generic;
namespace Sample
{
public class MyList<T> : IList<T>, IList
{
public T this[int index]
{
get { }
set { }
}
public IEnumerator<T> GetEnumerator() { }
object IList.this[int index]
{
get { return this[index]; }
set { this[index] = (T)value; }
}
IEnumerator IEnumerable.GetEnumerator()
{
return this.GetEnumerator();
}
}
}
|
上記のコードと同等の実装をC++/CLIで行おうとすると次のようになります。
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;
using namespace System::Collections;
namespace Sample
{
generic<class T>
public ref class MyList : Generic::IList<T>, IList
{
public:
property T default[int]
{
virtual T get(int index) sealed { }
virtual void set(int index, T value) sealed { }
}
virtual Generic::IEnumerator<T>^ GetEnumerator() sealed { }
protected:
property Object^ default[int]
{
virtual Object^ get(int index) sealed = IList::default::get
{
return this[index];
}
virtual void set(int index, Object^ value) sealed = IList::default::set
{
this[index] = safe_cast<T>(value);
}
}
virtual IEnumerator^ GetEnumerator() sealed = IEnumerable::GetEnumerator
{
return this->GetEnumerator();
}
};
}
|
しかしこのコードはコンパイルエラーになります。
error C3296: 'Sample::MyList<T>::default' : この名前のプロパティが既に存在します
error C2556: 'System::Object ^Sample::MyList<T>::default::get(int)' :
オーバーロード関数は、'T Sample::MyList<T>::default::get(int)' と戻り値の型のみが異なります。
error C2373: 'Sample::MyList<T>::default::get' : 再定義されています。異なる型修飾子です。
C++/CLIでは、たとえインタフェースの明示的実装であっても、戻り値の型のみが異なるようなオーバロードはできません。
ではC++/CLIで同名のプロパティやメソッドを持つインタフェースを同時に実装することはできないのかというとそんなことはありません。
実はC++/CLIでインタフェースの明示的実装を行うプロパティやメソッドの名前は、元の名前と同じである必要はないのです。
つまり、上述のコードを例えば次のように書き換えることができます。(変更部分のみ抜粋)
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
|
-
!
-
|
-
|
!
|
-
|
!
!
-
!
-
|
!
| protected:
property Object^ default_[int]
{
virtual Object^ get(int index) sealed = IList::default::get
{
return this[index];
}
virtual void set(int index, Object^ value) sealed = IList::default::set
{
this[index] = safe_cast<T>(value);
}
}
virtual IEnumerator^ GetEnumerator_() sealed = IEnumerable::GetEnumerator
{
return this->GetEnumerator();
}
|
この例では元の名前の末尾にアンダースコアを付けていますが、名前は何でも構いません。
C++/CLIでは名前付きインデクサを定義できるため、インデクサの別名による明示的実装も問題なくできます。
C++/CLIは意外と色々できるのですが、その書き方を見つけるまでがちょっと大変ですね。