web-dev-qa-db-ja.com

エラー:時代遅れの古いスタイルの基本クラス初期化子

次のコードは、C++ 98、C++ 11、およびC++ 14モードで、私が試したすべてのバージョンのGCCで後続のコンパイルエラーを生成します。

struct T
{
    T(void* x) : (x) {}
};

// main.cpp: In constructor 'T::T(void*)':
// main.cpp:3:18: error: anachronistic old-style base class initializer [-fpermissive]
//      T(void* x) : (x) {}
//                   ^
// main.cpp:3:16: error: unnamed initializer for 'T', which has no base classes
//      T(void* x) : (x) {}

確かに、私は実際には何も初期化していないので、明らかに壊れたコードです。

しかし、なぜそれが基本クラスの初期化子であり、なぜそれが単に間違っているのではなく「時代錯誤」であるのですか?かつては有効でしたか?いつ?そして、それはどういう意味ですか?


relのみ参照 これをWebで見つけたのは、メンバー名が誤ってマクロ化されたときにエラーに遭遇した人々であり、事実上、上記と同じコード:

#define bar
// ^ some library could have done this

struct T
{
    T(int x)
        : bar(x)   // effectively just `: (x)`
    {}

    int bar;       // will cause its own error
};

それらの人々は、エラーが何を意味するのか決して知りませんでしたが、少なくともプログラムが壊れた理由を後で発見しました。

最初のC++コンパイラであるCFrontの1984-5リリースのドキュメントにあります。

コンストラクタは次のように書くことができます:

  vec.vec(int lb, int hb) : (hb-lb+1)
  {
      if (hb-lb<0) hb = lb;
      low = lb;
      high = hb; 
  }

構成:(hb-lb + 1)は、基本クラスのコンストラクタvector()に必要な引数リストを指定するために使用されます。

あなたがそれについて考えれば、理にかなっています。おそらく、基本クラスの明示的な命名は、多重継承をサポートするために追加されました。 (当時、メンバー初期化リストはサポートされていませんでした-メンバーは無条件にデフォルトで構築されていました-したがって、多重継承の前に、コンストラクターがそこで初期化できるものは1つだけでした。)

ドキュメントをアーカイブするための http://www.softwarepreservation.org/projects/c_plus_plus/ へのクレジット。

...そしてすごい、私ちょうど今は「CFront」が言葉遊びであることに気づきました。

56
Sneftel

実際、これは有効な標準C++ではないため、言語の履歴の履歴を調べて、これが無効になった時点を見つける必要があります。

1989年、1985年にその名前で最初に開始されて以来、「C++」をさらに定義すると、Stroustrupは、多重継承に対処するために、基本初期化が言語の以前の化身から変更されたと宣言しました。

[p191] C++プログラミング言語[Stroustrup 1986]では、1985年8月に定義および実装されたC++について説明しています。この文書では、それ以降の言語の成長について説明し、定義のいくつかの点を明確にします。これらの言語の変更は拡張機能であることが強調されています。 C++は、長期的なソフトウェア開発に適した安定した言語であり続けます。 C++の主な新機能は次のとおりです:多重継承、タイプセーフリンケージ、オーバーロードされた関数のより良い解決、割り当てと初期化の再帰的定義、より良い機能ユーザー定義のメモリ管理、抽象クラス、静的メンバー関数、constメンバー関数、保護メンバー、演算子のオーバーロード->、およびメンバーへのポインター。 これらの機能はC++の2.0リリースで提供されています。

[p214]基本クラスとメンバーを初期化するための構文が拡張され、多重継承に対処できるようになり、初期化の順序がより正確に定義されました。 [..]

このテキストは、現在私たちが精通している基本クラスの初期化構文を示しています。Sneftelがすでに指摘しているように(古いドキュメントを探す手間が省けます)、これはそれほど遅くはありませんでした。 1985年、それ自体が「CwithClasses」から発展した元のC++実装。したがって、C++ 2.0は1989年により馴染みのある構文を導入し、この「時代錯誤」バージョンはそれまで有効であったと結論付けることができます。

もちろん、質問のコードにはベースがないことに注意してください。したがって、C++ 1.0でも、プログラムは最終的には正常にコンパイルされませんでした。ただし、構文がこのように解析されている理由を発見しました。

GCCが、C++のどの化身でも、ほぼ30年間有効ではなかった、あいまいで長い間忘れられていた構文を診断していることは注目に値します。


[1]「C++の進化:1985年から1989年」、Bjarne Stroustrup、AT&T Bell Laboratories 1989; pdf

これは、ARMのセクション18.3.2で時代錯誤として具体的に説明されています。

このような機能の理由は、通常、古いバージョンのC++またはCwithクラスに継続性を提供することでした。すべての「時代錯誤」には望ましくない特性がありました。コンパイラーはそのような機能を提供する必要はありませんでしたが、提供する場合は、プログラマーがそれを非アクティブ化すること、および/またはそれを使用することについて警告することを許可する義務がありました。

6
Peter