7

私は名前検索を非常によく理解していると思っていました (それに関するいくつかのビデオを見て、たくさん読んだ後) が、私はちょうどこのケースにぶつかりました:

#include <iostream>

namespace test{

  struct Id
  {};

  void do_something( const Id& ){  std::cout << "Hello, World!" << std::endl; }

  class Test
  {
  public:

    void do_something() { std::cout << "WTF!" << std::endl;  }

    void run()
    {
      Id id;
      do_something( id ); // doesn't compile
    }

  };

}

int main()
{
    test::Test my_test;
  my_test.run();
}

指摘された行は (GCC4.8 および VC11U2 で) コンパイルされません 。これは、唯一の可能な候補のように見えるtest::Test::do_something()名前空間スコープの代わりにメンバー関数を使用しようとするためです。test::do_something( const Id& )

どうやら、メンバー関数名は名前空間スコープの名前を隠しているようです。これは、この問題が発生することなく、他のコンテキストでほぼ同様のコードを使用したことを覚えているためです (ただし、最終的には条件が大きく異なる可能性があります)。

私の質問は、これらのコンパイラは標準に準拠していますか?

(残念ながら標準文書を読んでも名前の参照は非常にわかりにくいので、専門家の確認が必要です)

4

5 に答える 5

4

言語が構文の有効な解釈を見つけるために道を外れるほど、タイプミスやその他のそのような間違いにより、コンパイラーが有効であるが間違った意味を見つける可能性が高くなります。fooコンパイラは、 ifが何らかのスコープ内で定義されていて、そのスコープ内のコードが を使用していると想定していfooます。プログラマは、コードがスコープ内で定義されている を使用することを意図していfooます。プログラマーが、内部スコープ定義で許可されていない何かを実行しようとするとfoo、次のいずれかが当てはまる可能性が非常に高くなります。

  • プログラマーは、内部の foo で有効な、少し異なる他の操作を行うつもりでした。コンパイラーは、プログラマーが指定しない限り、プログラマーの意図を知ることは期待できません。
  • プログラマーは、指定された操作を実行するつもりでしたが、内部がサポートできないことに気付いていませfooんでした。したがって、内部がサポートできる他の操作または一連の操作を見つける必要がありますfoo。繰り返しになりますが、プログラマーが適切な使用方法を示さない限り、コンパイラーが優れたコードを生成することは期待できませんfoo
  • プログラマーは問題の操作を外側の foo で実行するつもりでしたが、明示的にそう言うことを拒否しました。これがプログラマーの意図したものであるとコンパイラーが推測したい場合、コンパイラーはこのように動作するコードを生成できます。

プログラマーの意図が #3 である場合にのみ、コンパイラーは意図したとおりに動作するコードを生成できる可能性があります。ただし、プログラマーが本当に 1 番または 2 番を意図していた可能性ははるかに高いです。#3 が有効なコードを生成すると仮定しても、コンパイラがコードのコンパイルを拒否する場合、上記の誤りのいずれかが検出され、したがって修正することができます。対照的に、コンパイラーができる限り #3 を仮定した場合、プログラマーが本当に意図した場合、コードが実行されて設計に反して動作するまで、#1 または #2 の問題は明らかになりません。

ところで、もし私にドラザーがいたら、この原則を .NET 言語の大文字と小文字の区別に適用し、定義と矛盾する方法で識別子を記述することを禁止するだけでなく (vb.net ではなく C# で行われているように)、ただし、大文字/小文字のみが内部スコープのものと異なる識別子の使用。例えば:

class foo
{
  int x;
  void bar()
  {
    int X=2;
    x=4; // ****
    return X;
  }
}

上記のコードを考えると、C# は、アスタリスクが付いた行がフィールドを書き込むためのものであると推測します。同様のコードが与えられた場合、vb.net は、ローカル変数を書き込むことを意図していると想定します。個人的には、どちらの仮定も嫌いです。「あなたの言いたいことを正確に言う」という原則は、コンパイラーがプログラマーにthis.x=4;またはX=4;のいずれかを言うことを要求する必要があることを私に示唆しています。

于 2013-05-22T16:56:38.187 に答える
2

これはルックアップの問題ではありません。重要な点は、オーバーロードの解決が開始される前にルックアップが完了することです。コンパイラはdo_something、それが何を意味するかを理解するためにルックアップを実行することを確認すると、それが関数であることを発見し、ADL をアクティブにして他の潜在的なオーバーロードを見つけます。その後、ルックアップが完了し、オーバーロードの解決が開始されます。過負荷の解決に失敗した場合。

于 2013-05-22T18:33:41.530 に答える
1

残念ながら標準文書を読んでも名前の参照は非常にわかりにくいので、専門家の確認が必要です

私は決して専門家ではありませんが、標準の名前検索規則を理解する方法は次のとおりです。

2 つの例:

void foo(int);

namespace associated
{
    struct bee {};
    void flower(bee);
}

namespace bar
{
    void foo();
    void flower();

    void test()
    {
        foo(42);                   // (A)
        flower(associated::bee()); // (B)
    }
}

int main()
{
    bar::test();
}

(A) [basic.lookup.unqual]: 「名前の宣言が見つかるとすぐに名前検索が終了する」ため、コンパイルされません。

(B) ADL のためにコンパイルします。associated関連する名前空間です。

ただし、[basic.lookup.argdep]/3 があります。

X を非修飾ルックアップ (3.4.1) によって生成されたルックアップ セットとし、Y を引数依存ルックアップ (次のように定義) によって生成されたルックアップ セットとします。Xが含まれている場合

  • クラスメンバーの宣言、または
  • using 宣言ではないブロック スコープの関数宣言、または
  • 関数でも関数テンプレートでもない宣言

Y は空です。それ以外の場合、Y は、以下で説明するように、引数の型に関連付けられた名前空間にある宣言のセットです。名前のルックアップによって検出される宣言のセットは、X と Y の和集合です。

最初のポイントはあなたの例に当てはまります。したがって、はい、あなたの例を拒否するコンパイラは標準に準拠していると思います。

于 2013-05-22T16:56:01.953 に答える