2020年8月9日日曜日

12: 「expected primary-expression before '>' token」GCCコンパイルエラー?

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

そのエラーメッセージは正しくないようであり、その背後にあるルールはリーズナブルでないようだ。. . .それはともかく、問題を解決しよう。

話題


About: C++

この記事の目次


開始コンテキスト


  • 読者は、C++の基本的知識を持っている。

ターゲットコンテキスト



  • 読者は、「expected primary-expression before '」「>」または何らかの「' token」C++ GCC コンパイルエラーを解決する方法を知る。

オリエンテーション


C++におけるテンプレートのコンセプトを解き明かす記事があります。.

C++における、定義と宣言の区別を解き明かす記事があります。


本体


1: 「expected primary-expression before '>' token」C++ GCCコンパイルエラーに遭遇す


Hypothesizer 7
「expected primary-expression before '>' token」?どういう意味だ?

えーと、私は、以下のC++コードをGCCでコンパイルしようとしている(言っておくが、私は、ファンクションテンプレート本体宣言たち(「定義」と呼ばれるべきでない)を'tpp'ファイル群に入れた)。

theBiasPlanet/coreUtilitiesTests/templatesTest1/ClassE.hpp

@C++ ソースコード
#ifndef __theBiasPlanet_coreUtilitiesTests_templatesTest1_ClassE_hpp__
#define __theBiasPlanet_coreUtilitiesTests_templatesTest1_ClassE_hpp__

namespace theBiasPlanet {
	namespace coreUtilitiesTests {
		namespace templatesTest1 {
			class ClassE {
				public:
				    ClassE ();
				    virtual ~ClassE ();
					template <typename U> U methodE0 (U const & a_argument0) const;
			};
		}
	}
}

#endif


theBiasPlanet/coreUtilitiesTests/templatesTest1/ClassE.cpp

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

namespace theBiasPlanet {
	namespace coreUtilitiesTests {
		namespace templatesTest1 {
			ClassE::ClassE () {
			}
			
			ClassE::~ClassE () {
			}
		}
	}
}


theBiasPlanet/coreUtilitiesTests/templatesTest1/ClassE.tpp

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

namespace theBiasPlanet {
	namespace coreUtilitiesTests {
		namespace templatesTest1 {
			template <typename U> U ClassE::methodE0 (U const & a_argument0) const {
				return a_argument0;
			}
		}
	}
}


theBiasPlanet/coreUtilitiesTests/templatesTest1/ClassF.hpp

@C++ ソースコード
#ifndef __theBiasPlanet_coreUtilitiesTests_templatesTest1_ClassF_hpp__
#define __theBiasPlanet_coreUtilitiesTests_templatesTest1_ClassF_hpp__
	
	#include "theBiasPlanet/coreUtilitiesTests/templatesTest1/ClassE.hpp"
	
	namespace theBiasPlanet {
		namespace coreUtilitiesTests {
			namespace templatesTest1 {
				class ClassF {
					public:
						template <typename U> static void methodF0 (U const & a_argument0);
						static void methodF1 (ClassE const & a_argument0);
				};
			}
		}
	}
#endif


theBiasPlanet/coreUtilitiesTests/templatesTest1/ClassF.cpp

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

#include <string>

namespace theBiasPlanet {
	namespace coreUtilitiesTests {
		namespace templatesTest1 {
			void ClassF::methodF1 (ClassE const & a_argument0) {
				a_argument0.methodE0 <std::string> ("aaa");
			}
		}
	}
}


theBiasPlanet/coreUtilitiesTests/templatesTest1/ClassF.tpp

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

#include <string>

namespace theBiasPlanet {
	namespace coreUtilitiesTests {
		namespace templatesTest1 {
			template <typename U> void ClassF::methodF0 (U const & a_argument0) {
				a_argument0.methodE0 <std::string> ("aaa");
			}
		}
	}
}

そのメッセージ内で言及されているその'>'トークンは、'a_argument0.methodE0 <std::string> ("aaa");'の'>'を指している。

そもそも、一体全体、「primary-expression」とは何のことだ?

このページは、それは、より複雑な表現の構成要素という意味だ、と主張している。. . . 分からないな . . .

なぜ分からないかというと、それなら、プライマリー表現でない表現なるものを私には想像できないから。実際、より複雑な表現の構成要素になり得ない表現に何がある?. . . 例えば、'1 + 2'という表現は、'1 + 2 + 3'の構成要素になれるので、それは、プライマリー表現であるはずだが、そのページは、'1 + 2'がプライマリー表現であることを認めていないように見える. . .

それはともかく、「MyClass」や「A::B」のような任意のクラス名や「A<int>」のような任意の「template id(テンプレートID)」はプライマリー表現である、とそのページが明示的に述べているのはありがたい。したがって、'std::string'は、間違いなくプライマリー表現であるわけでしょう?. . . それが本当にプライマリー表現であることに、私が見つけたいくつかの他のドキュメントも同意しているようだ。

えーと、私のコードで'>'の前にあるものは、. . . 「std::string」であって、プライマリー表現であると請け合ってくれている。. . . コンパイラーさん、プライマリー表現は既にそこにあるようですよ. . .


2: そのコンパイルエラーの解決を試みる


Hypothesizer 7
そのメッセージのずさんさはさておき、そのメッセージは、私の、テンプレートとの過去の格闘を思い出させた。

その時には、あるタイプ名がタイプ名であることをコンパイラに明示的に教えるために、'typename'を入れなければならなかったが、それは、'<'-'>'ペアが、テンプレートのパラメータ指定のためであって、ものを比較しているのでないことをはっきりさせるためであった。

えーと、コンパイラは'typename'を要求しているのだろうか?これではどうだろう?

@C++ ソースコード
				a_argument0.methodE0 <typename std::string> ("aaa");"

だめだ。

なぜだ?これで、'>'が比較オペレータでないことがコンパイラには分かるはずだろう?


3: そのコンパイルエラーを解決する、そして若干の苦情


Hypothesizer 7
実は、テンプレートタイプに依存した修飾下にあるテンプレートメンバーは、'.template'で、テンプレートであると明示的に宣言されなければならないというルールがあるようだ。

えーと、私の場合には、'methodE0 <U>'は'a_argument0'のテンプレートメンバであり、'a_argument0'はテンプレートタイプに依存した修飾である(確かに、それは修飾であり、'U'というテンプレートタイプに依存している)、したがって、それは、'a_argument0.template methodE0 <std::string> ("aaa")'でなければならない。

ふーむ. . . 、確かに、それで、この問題は解決した、しかし、そのようなルールはリーズナブルだろうか?. . . つまり、そのルールが存在することは理解するが、そのルールがなぜ馬鹿げていないのかを私は理解できない。

ある問題が満足いく説明なく解決したからといって、それだけでハッピーだという人間では私はない。なぜそのようなルールが必要なのかという説明を私は欲する。

例えば、以下(それは、既に上記コード中に存在する)はエラーを起こさない。

@C++ ソースコード
			void ClassF::methodF1 (ClassE const & a_argument0) {
				a_argument0.methodE0 <std::string> ("aaa");
			}

その理由は、勿論、その修飾がいかなるテンプレートタイプにも依存しておらず、当該ルールの前提から逃れていることだ。

しかし、エラー判定された行が、許可された行よりも、コンパイラにとってなぜ、より困難であるのか、いかなる理由も私には見えない。. . . あのね、テンプレートそのものは、コンパイルされることはなく、テンプレートのインスタンス化がコンパイルされるわけでしょう?実際、'void ClassF::methodF0 <ClassE> (ClassE const & a_argument0)'は、'void ClassF::methodF1 (ClassE const & a_argument0)'と何も違わないはずだ、メソッド名を除いては。

'<'および'>'の曖昧さを取り除くルールが必要かもしれないことは理解するが、現行のルールが賢明に選択されたか否か、私は疑問を持つ。

そのルールが存在しなければならないリーズナブルな必要性があるのであれば、理解可能な説明をしてくれるとありがたい。もしも、その説明が「私がそう言ったからお前はただ従わなければならない!」であるならば、そのようなルールは暴虐であり、愚かである、と私は見なすだろう。


参考資料


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