16

テスト スイートを作成しているときにoperator<<(std::ostream&...、Boost 単体テストを使用するための実装を提供する必要がありました。

これはうまくいきました:

namespace theseus { namespace core {
    std::ostream& operator<<(std::ostream& ss, const PixelRGB& p) {
        return (ss << "PixelRGB(" << (int)p.r << "," << (int)p.g << "," << (int)p.b << ")");
    }
}}

これはしませんでした:

std::ostream& operator<<(std::ostream& ss, const theseus::core::PixelRGB& p) {
    return (ss << "PixelRGB(" << (int)p.r << "," << (int)p.g << "," << (int)p.b << ")");
}

どうやら、g++ が演算子の使用を解決しようとしたときに、2 番目は一致候補に含まれていませんでした。なぜ(これを引き起こすルールは何ですか)?

コード呼び出しoperator<<は Boost 単体テスト フレームワークの奥深くにありますが、テスト コードは次のとおりです。

BOOST_AUTO_TEST_SUITE(core_image)

BOOST_AUTO_TEST_CASE(test_output) {
    using namespace theseus::core;
    BOOST_TEST_MESSAGE(PixelRGB(5,5,5)); // only compiles with operator<< definition inside theseus::core
    std::cout << PixelRGB(5,5,5) << "\n"; // works with either definition
    BOOST_CHECK(true); // prevent no-assertion error
}

BOOST_AUTO_TEST_SUITE_END()

参考までに、私は g++ 4.4 を使用しています (ただし、現時点では、この動作が標準に準拠していると想定しています)。

4

2 に答える 2

13

引数依存ルックアップ (koenig ルックアップの正しい名前) では、コンパイラはオーバーロードされた関数セットに、各パラメーターの名前空間で宣言されている関数を追加します。

あなたの場合、最初のものは、演算子を呼び出す引数のタイプであるoperator<<名前空間で宣言されています。thesus::core,したがって、これoperator<<は関連付けられた名前空間で宣言されているため、ADL と見なされます。

2 番目のケースでは、パラメータ 1 が名前空間の型であり、パラメータ 2 がoperator<<名前空間の型であるため、関連付けられた名前空間ではないグローバル名前空間で が宣言されているようです。stdtheseus::core

実際、おそらくあなたの2番目operator<<は、親スコープを調べることで見つかるため、グローバル名前空間で宣言されていません..多分あなたはこのようなものを持っていますか? より多くのコードを投稿していただければ、より良い回答を提供できます。


ADL は、現在のスコープで名前が見つかった場合、親スコープで検索しないことを思い出しました。そのため、boost マクロBOOST_TEST_MESSAGEは を含むように展開さoperator<<れ、スコープ ツリーにはoperator<<、式とグローバル スコープの間に実行不可能な a がいくつかあります。これを説明するためにコードを更新しました(うまくいけば)。

#include <iostream>

namespace NS1
{
  class A
  {};

  // this is found by expr in NS2 because of ADL
  std::ostream & operator<<(std::ostream &, NS1::A &);
}


// this is not seen because lookup for the expression in NS2::foo stops when it finds the operator<< in NS2
std::ostream & operator<<(std::ostream &, NS1::A &);

namespace NS2
{
    class B
    {};

    // if you comment this out lookup will look in the parent scope
    std::ostream & operator<<(std::ostream &, B &);

    void foo(NS1::A &a)
    {
        std::cout << a;
    }  
}
于 2011-01-05T12:34:07.307 に答える
1

演算子のオーバーロードは関数に似ていますが異なります。違いの 1 つは名前空間の検索です。

関数と同様に、演算子のオーバーロードは名前空間に属しますが、関数をスコープする方法は非現実的です。コードが呼び出す必要があると想像してください

std::cout thesus::core::<< p; // ouch and obviously incorrect syntax

したがって、<<オペレータは、パラメータの 1 つのネームスペースstd( の場合cout) または のネームスペース (pこの場合は ) にある必要がありますthesus::core

これが Koenig Lookup の原則です。オペレーターのオーバーロードを正しい名前空間で定義する必要があります。

于 2011-01-05T12:53:16.460 に答える