あるクラスコンストラクタテンプレートを明示的にインスタンス化するよう意図されたある表現は、Visual C++では得体の知れないエラー群を引き起こします、GCCでは問題ないのであるが。
話題
About: C++
About: Visual C++
About: テンプレート
この記事の目次
開始コンテキスト
- 読者は、C++の基本的知識を持っている。
- 読者は、C++テンプレートとは何であるか、およびテンプレートインスタンス化(明示的にか暗黙的にか)とインライン化の区別を理解している。
ターゲットコンテキスト
- 読者は、クラスコンストラクタテンプレートを明示的にインスタンス化するよう意図されたある種の表現は、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++標準は、コンストラクタの、ファンクション名のように思えるものは、実際にはファンクション名ではないと主張しているようだが、そのような主張は全く恣意的であるように思われる、コンストラクタテンプレートの宣言は非コンストラクタテンプレートの宣言と全く同様なのに、なぜ、コンストラクタテンプレートの明示的インスタンス化は違うものでなければならないのか?)。