2

このプログラムをコンパイルするとき、operator<< 呼び出しがグローバル名前空間の名前に解決されることを期待していましたが、代わりに、コンパイラがあいまいなオーバーロードを報告します。引数依存のルックアップにより、潜在的な一致として含まれる名前空間内の関数の前に、非依存のルックアップが発生したと思いました。これは、非テンプレート関数の場合のようです。

誰か説明できますか?

#include <iostream>

class Foo
{};

namespace NS
{
    class Stream
    {};

    template <typename T>
    Stream& operator << ( Stream& s, T t)
    {
        std::cerr << "Namespace call!\n";
        return s;
    }
}

template<typename STREAM>
STREAM& operator << ( STREAM& s, Foo f )
{
    std::cerr << "Global NS call";
    return s;
}

/**
* This function (as opposed to the one above) is not ambiguous.  Why?

NS::Stream& operator << ( NS::Stream& s, Foo f )
{
    std::cerr << "Global NS call";
    return s;
}

*/

int main()
{
    Foo f;
    NS::Stream s;

    s << f;
    return 0;
}

コンパイラ出力:

test11.cpp: In function ‘int main()’:
test11.cpp:28: error: ambiguous overload for ‘operator<<’ in ‘s << f’
test11.cpp:18: note: candidates are: STREAM& operator<<(STREAM&, Foo) [with STREAM = NS::Stream]
test11.cpp:13: note:                 NS::Stream& NS::operator<<(NS::Stream&, T) [with T = Foo]
4

4 に答える 4

3

には 2 つの候補がありs << fます。グローバルなものとネームスペース化されたものです。C++ コンパイラの場合、これら 2 つのどちらかを選択するものは何もないため、あいまいです。

于 2011-04-20T18:16:21.117 に答える
2

これは古い質問ですが、OPの特定の質問に関してまだ明確になっていないことがいくつかあると思いますので、ここに行きます。

まず第一に、引数依存のルックアップと通常の非修飾ルックアップの両方が、修飾されていない関数と演算子の呼び出しに対して行われます。これは、通常の関数と関数テンプレートの特殊化の両方に適用されます。[3.4.2 パラグラフ 3] によると、唯一の例外は、通常の非修飾ルックアップが見つかった場合です。

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

上記の場合のみ、引数依存のルックアップは実行されません。ご覧のとおり、このケースにはこれらのどれも当てはまりません。

したがって、両方の宣言が見つかります。ここで、最適な実行可能な関数を選択するために、オーバーロードの解決を実行する必要があります。どちらの場合も、引数はパラメーターの型に完全に適合するため、より適切な変換に基づいて、あるオーバーロードを別のオーバーロードよりも選択することはできません。どちらもテンプレートの特殊化であるため、最後の手段として、関数テンプレートの部分的な順序付けを使用して、一方が他方よりも特殊化されているかどうかを判断しようとします。残念ながら、NS のテンプレートは最初の引数により特化されており、グローバル テンプレートは 2 番目の引数により特化されているため、他のテンプレートよりも特化されたテンプレートはありません。結論:オーバーロードを選択することはできません。呼び出しはあいまいです。

さて、2 番目の質問ですが、コメント アウトされた演算子の定義についてです。その定義をコメント解除すると、この場合も ADL が実行されます。3 つのオーバーロードはすべて、名前の検索によって検出されます。ここでも、すべての引数がパラメーターの型と完全に一致しています。異なるのは、最後の定義がテンプレートではなく通常の演算子関数であることです。変換に基づいて他のオーバーロードを選択できない場合、1 つが通常の関数であり、他のすべてがテンプレートの特殊化である場合、非テンプレートのものが他のものよりも優先されます。そのため、この場合、呼び出しがあいまいではなくなりました。


標準的な参照は、公開前の最後の C++14 ドラフトである N4140 に対するものですが、C++03 以降、上記のいずれも変更されていないと思います。

于 2015-01-04T17:39:41.180 に答える
1

グローバル名前空間には特別な優先順位はありません。の問題s << fは、両方の引数が名前空間に関連付けられていることです: swith::NSfwith ::.

グローバル名前空間が他の名前空間とまったく同じであることを考えると (ここでは関係なく、常にスコープ内にあることを除いて)、2 つの関数のオーバーロードは最適に一致するように正確に結合され、コンパイラができることは何もありません。

IOStreams ライブラリを使用する場合、この問題は、テンプレートのパラメーター化を行わずにistream &または型のパラメーターを受け入れることで解決されます。ostream &

于 2011-04-20T18:57:58.093 に答える
0

Stream内部で定義しているため、あいまいさがありますnamespace NS。グローバル名前空間で定義するStreamと、あいまいさはなくなります。

コンパイラは、修飾されていない関数への引数とそれに関連付けられた名前空間に従って、選択する関数を解決しようとします。ISO/IEC 14882:2003 標準のセクション 3.4.2 - 引数依存の名前検索を参照してください。1 つの引数がグローバル名前空間で定義され、1 つの引数が NS で定義されているため、コンパイラはどの関数を使用するかを認識していません。

于 2011-04-20T18:23:28.097 に答える