2021年8月8日日曜日

23: Visual C++で、コンストラクタテンプレートを明示的にインスタンス化する

<このシリーズの前の記事 | このシリーズの目次 | このシリーズの次の記事>

あるクラスコンストラクタテンプレートを明示的にインスタンス化するよう意図されたある表現は、Visual C++では得体の知れないエラー群を引き起こします、GCCでは問題ないのであるが。

話題


About: C++
About: Visual C++
About: テンプレート

この記事の目次


開始コンテキスト



ターゲットコンテキスト



  • 読者は、クラスコンストラクタテンプレートを明示的にインスタンス化するよう意図されたある種の表現は、Visual C++では許されないこと、および用いるべき代替の表現を知る。

オリエンテーション


Visual C++の奇癖について、他にいくつかの記事があります(ある"unable to match function definition to an existing declaration"エラーDLLからシンボル群をエクスポートする).


本体

ト書き
Hypothesizer 7は独白する。


1: あるクラスコンストラクタテンプレートの明示的インスタンス化に対する得体の知れないエラー群に遭遇する


Hypothesizer 7
以下のクラスは、1つのコンストラクタテンプレートを持っている('.cpp'はここには示されていない)。

'theBiasPlanet/coreUtilitiesTests/templatesTest1/ClassA.hpp'

@C++ ソースコード
#ifndef __theBiasPlanet_coreUtilitiesTests_templatesTest1_ClassA_hpp__
	#define __theBiasPlanet_coreUtilitiesTests_templatesTest1_ClassA_hpp__
	
	namespace theBiasPlanet {
		namespace coreUtilitiesTests {
			namespace templatesTest1 {
				template <typename T> class ClassA {
					public:
					    ClassA ();
					    template <typename U> ClassA (U a_argument0);
				};
			}
		}
	} 
#endif

以下は、そのクラスコンストラクタテンプレートを明示的にインスタンス化するよう意図されている。

'theBiasPlanet/coreUtilitiesTests/templatesTest1/TemplatesInstantiator.cpp'

@C++ ソースコード
#include "theBiasPlanet/coreUtilitiesTests/templatesTest1/ClassA.tpp"

using namespace ::theBiasPlanet::coreUtilitiesTests::templatesTest1;

template class ClassA <int>;
template ClassA <int>::ClassA <double> (double a_argument0); // This causes a series of errors for Visual C++

GCCは、それを問題なくコンパイルする。

Visual C++は、それを非難する、以下のエラー群を出して: 「error C2039: 'theBiasPlanet::coreUtilitiesTests::templatesTest1::ClassA<double>': is not a member of 'theBiasPlanet::coreUtilitiesTests::templatesTest1::ClassA<int>'」および「error C2062: type 'double' unexpected」。

はあ?どういう意味だ?


2: 1つの解決策と考察


Hypothesizer 7
どうやら、Visual C++は、クラスコンストラクタテンプレートのタイプパラメータ値を明示的に指定することを許さないようだ。

実は、Visual C++は、以下は受け入れる。

@C++ ソースコード
template ClassA <int>::ClassA (double a_argument0);

タイプパラメータ値群は、引数群から推論されるよう意図されているようだ、しかし、もしも、あるタイプパラメータ値が引数群から推論できなかったらどうなるのか、以下のように?

'theBiasPlanet/coreUtilitiesTests/templatesTest1/ClassB.hpp'

@C++ ソースコード
#ifndef __theBiasPlanet_coreUtilitiesTests_templatesTest1_ClassB_hpp__
	#define __theBiasPlanet_coreUtilitiesTests_templatesTest1_ClassB_hpp__
	
	namespace theBiasPlanet {
		namespace coreUtilitiesTests {
			namespace templatesTest1 {
				template <typename T> class ClassB {
					public:
					    ClassB ();
					    template <typename U> ClassB ();
				};
			}
		}
	} 
#endif

意味がない?必ずしもそうとは言えない: そのタイプ、「U」は、コンストラクタテンプレート実装の中で有意義に使われているかもしれない。

明らかに、Visual C++のやり方は、そのコンストラクタテンプレートの明示的インスタンス化を全く許さない。

明示的にタイプパラメータ値を指定するやり方は、当該コンストラクタテンプレートをインスタンス化できるが、実のところ、そのコンストラクタテンプレートは、結局のところ、使うことができない。

その理由は、実際問題、それはどうやって使えるのか? . . . C++は以下を許さない。

@C++ ソースコード
	ClassB <int> <double> l_classB (); // This is not allowed, where 'double' is meant to be a specification of 'U'.

C++標準の主張は、「コンストラクタが呼ばれる方法が故に、それはできないのだ」ということのようだが、それはC++標準の欠陥にすぎないと私は思う: C++標準は、方法を考案すればよいだけのことだ、なぜなら、あなたがマスターなのだから!

勿論、本当に窮したら、ダミー引数を追加することはできるが、それではとても醜いことになるだろう。

Visual C++のやり方がC++標準により忠実なのか否か私は知らないが、いずれにせよ、それはアンリーズナブルである、非コンストラクタメソッドテンプレートのための表現と非対称であって(C++標準は、コンストラクタの、ファンクション名のように思えるものは、実際にはファンクション名ではないと主張しているようだが、そのような主張は全く恣意的であるように思われる、コンストラクタテンプレートの宣言は非コンストラクタテンプレートの宣言と全く同様なのに、なぜ、コンストラクタテンプレートの明示的インスタンス化は違うものでなければならないのか?)。


参考資料


<このシリーズの前の記事 | このシリーズの目次 | このシリーズの次の記事>