0

以下の例では、次のコンパイル済みエラーが発生します。

test.cpp: In function ‘int main(int, char**)’:
test.cpp:26:8: error: no match for call to ‘(Derived) (p1&)’
test.cpp:14:8: note: candidate is:
test.cpp:16:10: note: void Derived::operator()(const p2&)
test.cpp:16:10: note:   no known conversion for argument 1 from ‘p1’ to ‘const p2&’

これはC++11で変更されているので、usingステートメントを入力する必要がないことを理解していました。それは正しくありませんか?これを回避する他の方法はありますか?

例(--std = c ++11を使用してgcc4.7でコンパイル):

#include <iostream>
#include <string>

using namespace std;

struct p1{};
struct p2{};

struct Base
{
    void operator()(const p1&) { cout << "p1" << endl; }
};

struct Derived : public Base
{
    void operator()(const p2&) { cout << "p2" << endl; }
    //Works if I include: using Base::operator();
};

int main(int argc, char** argv)
{
    p1 p;
    p2 pp;
    Derived d;

    d(p);
    d(pp);
}
4

3 に答える 3

3

これは C++11 で変更されていると理解していたので、using ステートメントを入れる必要はありませんでした。それは正しくありませんか?

いいえ、メンバー関数は C++11 でも非表示にすることができます。

これを回避する他の方法はありますか?

宣言を使用することは、意図された救済策です。

于 2012-07-12T22:04:09.550 に答える
3

私の知る限りでは、これは C++11 でも変更されていません。

そしてそれが変わっていない理由は、この行動が偶然ではないからです。この言語は、設計上、このように機能します。これには長所と短所がありますが、標準化委員会のメンバーが忘れていたために起こったことではありません。

いいえ、それを回避する方法はありません。これは、C++ でメンバー検索がどのように機能するかです。

于 2012-07-12T22:04:54.637 に答える
2

状況を明確にするために、これが C++ で変化することは想像できませ。それを支持できる変更にすることを望んでも、C との互換性がなくなるまで型の安全性を強化する必要があります (たとえば、すべての暗黙的な変換をほぼ排除する必要があります)。

状況はかなり単純です。現在、名前の検索は、使用されている名前のインスタンスが少なくとも 1 つ含まれている最初のスコープで停止します。その名前のインスタンスが、名前を使用しようとした方法と一致しない場合、コンパイルは失敗します。

その時点で検索を停止する代わりに、コンパイラはスコープの検索を続行し、本質的にそれらすべての名前のオーバーロード セットを作成してから、最も一致するものを選択します。

このような場合、外部スコープでの一見些細な変更が、まったく意図せずにコードの意味を (完全に) 変更する可能性があります。たとえば、次のように考えてください。

int i;

int main() { 
    long i;

    i = 1;

    std::cout << i;
    return 0;
}

現在の規則では、これの意味は明確で明確です。 は、定義されたローカルの toi=1;に値 1 を代入します。imain

改訂された規則の下では、それは疑問の余地があります-実際、おそらくそうであるべきではありません. コンパイラは と の両方のインスタンスを検出しi1タイプが であるため、代わりintにグローバルと一致する可能性があります。iを出力するiと、コンパイラは を受け取るオーバーロードを検出するlongため、ローカルi(まだガベージが含まれています) を出力します。

ただし、これにより別の問題が追加されることに注意してください。ローカルで動作する可能性のあるオーバーロードがあったためcout << i;、ローカルを参照することになります。したがって、使用されたオーバーロードを制御する変数の型の代わりに、使用された変数も制御する利用可能なオーバーロードを持つことになります。よくわかりませんが、これにより解析がはるかに困難になると思います(おそらく、NP困難またはNP完全な問題です)。i

要するに、(意図的またはその他の方法で) ほぼすべての種類の暗黙的な変換を内部スコープで使用するコードは、外部スコープでのこの一見無関係な変更により、そのコードの意味が突然完全に変更される可能性があり、上記の例のように、簡単に壊れることがあります。その過程でかなり徹底的に。

上記の例では、わずか 6 行のコードで、何が起こっているのかを簡単に把握できます。ただし、(たとえば) ヘッダーでクラスを定義し、そのヘッダーを他のファイルにインクルードするとどうなるかを考えてみてください。コンパイラは、ヘッダーをインクルードした他のコードを調べ、より適切なコードを見つけ、突然コーディングします。あなたは、徹底的に吟味され、最も見事な休憩をテストしたと誓った.

ただし、ヘッダーを使用すると、さらに悪化する可能性があります (または、少なくとも可能性があります)。クラスを定義し、ヘッダーを 2 つの異なるファイルに含めます。これらのファイルの 1 つは外部スコープで変数、関数などを定義し、もう 1 つは定義しません。Xという名前を使用する1つのファイルのコードはグローバルを参照し、他のファイルのコードはローカルを参照します。これは、グローバルがそのファイルに表示されないためです。これにより、モジュール性が完全に失われ、事実上すべてのコードが完全に壊れてしまいます (少なくとも壊れる可能性があります)。

もちろん、これほど壊れていない可能性は他にもあります。1 つの可能性は、すべての暗黙的な変換を排除することです。そのため、完全な型の一致のみが考慮されます。これにより、明らかな問題のほとんどが解消されますが、 C との互換性が完全に失われるという代償が伴います (言うまでもなく、おそらく多くのプログラマーが不満を抱くでしょう)。もう 1 つの可能性は、現在のように検索し、一致が見つかった最初のスコープで停止することです。次に、内側のスコープでその名前を使用した場合にコンパイルが失敗する場合にのみ、外側のスコープに進みます。

どちらでも機能しますが、(少なくとも) 非常識なレベルの混乱を招かないようにするには、かなりの制限が必要です。たとえば、a =1; a = '\2';今のところ、これらは同じ変数を参照する必要がありますが、最初のルールではそうではありません。

いくつかの特殊なケースでは、おそらくその特定の奇妙さを排除することもできます。たとえば、変数名の検索には現在のルールを使用し、関数名のみに新しい/別のルールを使用します。

要約: これを行うための単純で明白な方法は、ほとんど取り返しのつかないほど壊れた言語を作成します。それを防ぐための変更は可能ですが、C と本質的にすべての既存の C++ コードの両方との互換性を捨てることを犠牲にしてのみです。後者は、まったく新しい言語で可能かもしれませんが、C++ のようにすでに確立されている言語 (特に、後方互換性に基づいて確立された言語、つまり C++) では可能です。

于 2012-07-12T23:46:59.903 に答える