そのエラーは、多分、「2ステージネームルックアップ」によるものだ、それが導入されたのは賢明でなかったが、それでも、それを学ばなければならない。
話題
About: C++
この記事の目次
- 開始コンテキスト
- ターゲットコンテキスト
- オリエンテーション
- 本体
- 1: 「there are no arguments to ~ that depend on a template parameter, so a declaration of ~ must be available」コンパイルエラーに遭遇す
- 2: 実は「2ステージネームルックアップ」の1ケースに私は遭遇させられたのだ
- 3: 1つの解決策
開始コンテキスト
- 読者は、C++の基本的知識を持っている、たとえ、その、広く誤って伝えられているいくつかの要素を正確に理解していないとしても。
- 読者は、C++ 'テンプレート'とは本当には何であるか、およびその使い方の正確な知識を持っている。
ターゲットコンテキスト
- 読者は、「2ステージネームルックアップ」のメカニズムおよび「there are no arguments to ~ that depend on a template parameter, so a declaration of ~ must be available [-fpermissive]」エラーをどのように解決するかを知る。
オリエンテーション
他のいくつかの賢明でないC++の決定を論じたいくつかの記事があります、テンプレートに関するこれ、'const'に関するこれ、クラススタティックフィールドに関するこれ等。
本体
ト書きHypothesizer 7は、独白する。
1: 「there are no arguments to ~ that depend on a template parameter, so a declaration of ~ must be available」コンパイルエラーに遭遇す
Hypothesizer 7
「there are no arguments to ‘methodC0’ that depend on a template parameter, so a declaration of ‘methodC0’ must be available」?
はあ?
私のコードは以下のものだ。
'theBiasPlanet/tests/templatesTest1/ClassC.hpp'
@C++ ソースコード
#ifndef __theBiasPlanet_tests_templatesTest1_ClassC_hpp__
#define __theBiasPlanet_tests_templatesTest1_ClassC_hpp__
namespace theBiasPlanet {
namespace tests {
namespace templatesTest1 {
template <typename T> class ClassC {
public:
int methodC0 (int a_argument0);
};
}
}
}
#endif
'theBiasPlanet/tests/templatesTest1/ClassC.tpp'
@C++ ソースコード
#include "theBiasPlanet/tests/templatesTest1/ClassC.hpp"
namespace theBiasPlanet {
namespace tests {
namespace templatesTest1 {
template <typename T> int ClassC <T>::methodC0 (int a_argument0) {
return a_argument0;
}
}
}
}
'theBiasPlanet/tests/templatesTest1/ClassD.hpp'
@C++ ソースコード
#ifndef __theBiasPlanet_tests_templatesTest1_ClassD_hpp__
#define __theBiasPlanet_tests_templatesTest1_ClassD_hpp__
#include "theBiasPlanet/tests/templatesTest1/ClassC.hpp"
namespace theBiasPlanet {
namespace tests {
namespace templatesTest1 {
template <typename T> class ClassD : public ClassC <T> {
public:
int methodD0 (int a_argument0);
};
}
}
}
#endif
'theBiasPlanet/tests/templatesTest1/ClassD.tpp'
@C++ ソースコード
#include "theBiasPlanet/tests/templatesTest1/ClassD.hpp"
namespace theBiasPlanet {
namespace tests {
namespace templatesTest1 {
template <typename T> int ClassD <T>::methodD0 (int a_argument0) {
return methodC0 (a_argument0);
}
}
}
}
Additions to 'theBiasPlanet/tests/templatesTest1/TemplatesInstantiator.cpp'
@C++ ソースコード
#include "theBiasPlanet/tests/templatesTest1/ClassD.tpp"
template class ClassD <int>;
エラーは、'ClassD.tpp'中の「return methodC0 (a_argument0);」行をエラー判定している。
そのエラーメッセージは愚かしいのではないかと疑っている方がもしもおられるのであれば、その人を私は安心させてあげたい、確かにそうであると。
第1に、「there are no arguments to ‘methodC0’ that depend on a template parameter(テンプレートパラメータに依存する‘methodC0’に引数がない)」であろうがなかろうが、「a declaration of ‘methodC0’(‘methodC0’の宣言)」は、利用可能でなければならないでしょうが? . . . そのメッセージは、まるで、テンプレートパラメータ依存引数を持つファンクションテンプレートコールは何らの宣言も必要としないかのようだが、それは私のC++の理解に全く反している。
第2に、そのメッセージが「引数」にフォーカスしているのは的外れである。 . . . そのファンクションテンプレートは引数を全然持たないので、勿論、それは、テンプレートに依存する引数を持っていない。だから何?ダミー引数を追加しなければならないとかいうことなのだろうか?愚かしい!
第3に、宣言は実際に利用可能になっていると考えざるを得ない、それは、'ClassC.hpp'内にあり、'ClassD.hpp'を介してインクルードされているのだから。 . . . えーと、今は、賢明でない「2ステージネームルックアップ」によればその宣言は利用可能であると認識されないということを私は知っているが、それにしても、なんと非協力的なメッセージであることか!
2: 実は「2ステージネームルックアップ」の1ケースに私は遭遇させられたのだ
Hypothesizer 7
実は、C++委員会は、ある悪いアイデアに襲われたのだ: 「2ステージネームルックアップ」。
えーと、その悪いアイデアは、ここで悪く説明されている。
そのドキュメントの1つの悪い点は、不可欠な2つの側面の仕様を明確に述べないことだ: 1) 第1ステージにてどのネームたちが解決されることになるのか 2) 第1ステージにてどのエンティティたちへ向けて解決が行なわれ(どのエンティティたちが名前によって指され)うるのか。
実際、それは、「This distinction between lookup of dependent and non-dependent names is called two-stage (or dependent) name look up(依存名のルックアップと非依存名のルックアップの間のこの区別が2ステージ(または依存)ネームルックアップと呼ばれる)」へ来るまで、第1の側面をのみを説明している: 「依存」であることは、ネームが第ステージで解決されないことの条件であり、それ以外の何物でもない。
しかし、それは、突然に言い放つ、「It will not look into the base class, since that is dependent(そのベースクラスの中は見られない、なぜならそれは依存しているから)」。 . . . はあ?第2の側面において依存クラスの中は見られないなんて私は知らなかったよ。 . . .
えーと、第1の側面におけるルールと第2の側面におけるルールの間には、一貫性があるように見えない。
第1の側面において、「return i;」中のその「i」は、パラメータ付けされた'struct'テンプレートの中にいるにもかかわらず、「依存」でないと見なされているようだが、それは、私には、全く、賛成できないことだ: 私は、パラメータ付けされたブロック内はどれも全体として依存コンテキストだと考える。 . . . それが、当該メカニズムにおける大きなつまずきの石だ。 . . . 私の常識では、その「i」は「this->i」か「Derived <T>::i」以外の何物でもない、明示的にそう表現されていなくても。私は、グローバル変数を使うことを常に避けるので、グローバル変数などそこに予期などしない、したがって、他の何でありうるというのか?
いずれにせよ、他方で、第2の側面において、「int i;」中の「i」は、それに向けて解決が行なわれる資格がなく、その理由は、それがパラメータ付けされた'struct'テンプレートの中にあるから? . . . なぜ、パラメータ付けされたブロック内にいることが問題であり始めたわけ、第2の側面のためにだけ? . . . そのドキュメントは、「you may declare specializations of Base even after declaring Derived(あなたは、Baseの特殊化を、Derivedを宣言した後にさえも、宣言するかもしれない)」と述べるが、だから何ですか? . . . 'Derived'を宣言した後で'Base <int>'、'Base <double>'、'Base <::std:string>'等を私が宣言すると仮定して、なぜ、「so the compiler cannot really know what i would refer to(したがって、コンパイラーはiが何を参照するのかを本当に知ることができない)」のか私には見えない、だって、コンパイラーは、'Derived <T>'中の「i」に、'Base <T>'中のその「i」を参照させればよいだけだから、私の意見では。 . . . そのドキュメントが何を心配しているのかが私には見えない。
当メカニズムの推奨者たちは テンプレートのいわゆる「インスタンス化」(その用語のその使用法に私は反対する)において名前が解決されるのが問題であるというアイデアに襲われたらしいが、そこに私は何の問題も見ない: 名前がその時点で解決されるというのは、とても自然であるという以外の何物でもない。
当メカニズムは グローバルなエンティティたちの解決を気にかけているようだが、なぜグローバルなエンティティたちにそのような優先度が与えられているのか、私はいぶかる。私にとっては、グローバルなエンティティたちは、避けるべき困り者であって、それら困り者たちへのそうしたひいきのために、ベースクラスアクセスのようなたしなみのよい行為が妨げられている。
3: 1つの解決策
Hypothesizer 7
当メカニズムは賢明でないが、私はプロジェクトをコンパイルさせないといけない。
どうやって?えーと、エラーメッセージは紛らわしくも「arguments」について語っているが、それは全然、論点ではない。論点は、'methodC0'コールは、依存でないとみなされており、グローバルファンクションであると決めつけられていることだ。
したがって、私はその'methodC0'コールを「dependent(依存)」にしなければならない、'this->methodC0 ()'のように。
. . . 愚かしいルールだ、と私は言う。