環境
- C++11
- Visual Studio 2017
- gcc version 5.4.0
内容
下記のような親子関係があるテンプレートクラスを Curiously Recurring Template Pattern で作りました。
#include <iostream>
// 親クラス
template <class Child, class SomeType>
struct Base
{
void interface()
{
static_cast<Child *>(this)->implementation();
}
void set_value(SomeType value)
{
member_ = value;
}
public:
SomeType member_;
};
// 子クラス
template <class SomeType>
struct Derived : Base<Derived<SomeType>, SomeType>
{
void implementation()
{
std::cout << member_ << std::endl;
}
};
int main(int argc, char *argv[])
{
Derived<int> obj;
obj.set_value(1);
obj.interface();
}
このコードは VC++ では問題なく通り、実行すると 1
とメンバ変数 member_
の値が出力されます。
しかし、gcc でビルドすると以下のコンパイルエラーが発生します。
error: ‘member_’ was not declared in this scope
std::cout << member_ << std::endl;
^
this ポインタから参照するように修正すると、gcc でもコンパイルが通ります。
- std::cout << member_ << std::endl;
+ std::cout << this->member_ << std::endl;
自分の理解では、public で継承しているので、親クラスでアクセス修飾子が protected である member_ には、子クラスからもアクセスできるはずとの認識ですが、なぜ変数が定義されていないとエラーになってしまうのでしょうか。
聞きたいこと
- gcc でコンパイルエラーとなる理由
- C++ の仕様としては「コンパイルが通る」または「コンパイルエラーとなる」のどちらが正しいのか
すみませんが、よろしくおねがいします。
-
気になる質問をクリップする
クリップした質問は、後からいつでもマイページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
クリップを取り消します
-
良い質問の評価を上げる
以下のような質問は評価を上げましょう
- 質問内容が明確
- 自分も答えを知りたい
- 質問者以外のユーザにも役立つ
評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。
質問の評価を上げたことを取り消します
-
評価を下げられる数の上限に達しました
評価を下げることができません
- 1日5回まで評価を下げられます
- 1日に1ユーザに対して2回まで評価を下げられます
質問の評価を下げる
teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。
- プログラミングに関係のない質問
- やってほしいことだけを記載した丸投げの質問
- 問題・課題が含まれていない質問
- 意図的に内容が抹消された質問
- 広告と受け取られるような投稿
評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。
質問の評価を下げたことを取り消します
この機能は開放されていません
評価を下げる条件を満たしてません
質問の評価を下げる機能の利用条件
この機能を利用するためには、以下の事項を行う必要があります。
- 質問回答など一定の行動
-
メールアドレスの認証
メールアドレスの認証
-
質問評価に関するヘルプページの閲覧
質問評価に関するヘルプページの閲覧
checkベストアンサー
+3
こんにちは。
それは、標準規格通りの動作です。
テンプレート引数が確定しないと(実体化しないと)確定しないものは、最初の名前解決の時には無視されるようです。そして、それらは次のフェーズである実体化処理時に名前解決されます。
Base<Derived<SomeType>, SomeType>
は、SomeTypeが決まらないと決まりません。なので、そのメンバ変数のmember_も確定しません。(仮にint型だったとしても同様です。Baseが特殊化されていて、特殊化の方にはmember_がないことも許されますから。)
しかし、使う側は member_ で呼び出しているだけで特に限定する修飾がなく、テンプレート引数に依存していません。結果、これは最初の名前解決の際にマッチングされます。
つまり、最初の名前解決時、使う側は「あるもの」なので名前解決しようとします。しかし、その時、規定クラスの member_ はテンプレート引数に依存しているので無視され、名前解決エラーになります。
使う際に「this->member_」とすると、これはthisのメンバという限定修飾をしています。thisはテンプレート引数に依存しているので、最初の名前解決では同じく無視され、実体化時に名前解決されるので両者めでたくマッチングされます。
このような挙動は、Two phase name lookupと呼ばれるそうです。下記に詳しい記述がありました。
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
+2
これはエラーになるのが正しいです。 その理由については詳しく説明するとちょっと長くなるのですが、よく知られた問題でもあるのでちょうどよい説明を探してきました。 参照してください。
投稿
-
回答の評価を上げる
以下のような回答は評価を上げましょう
- 正しい回答
- わかりやすい回答
- ためになる回答
評価が高い回答ほどページの上位に表示されます。
-
回答の評価を下げる
下記のような回答は推奨されていません。
- 間違っている回答
- 質問の回答になっていない投稿
- スパムや攻撃的な表現を用いた投稿
評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。
15分調べてもわからないことは、teratailで質問しよう!
- ただいまの回答率 89.99%
- 質問をまとめることで、思考を整理して素早く解決
- テンプレート機能で、簡単に質問をまとめられる
2019/03/14 14:03
名前解決に失敗した原因と、this を付けたら名前解決できた理由について理解できました。