Правила поиска операторов C ++ / Поиск Кенига - PullRequest
13 голосов
/ 05 января 2011

При написании набора тестов мне нужно было предоставить реализацию 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 ++ пытался разрешить использование оператора. Почему (какое правило вызывает это)?

Код, вызывающий 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 (хотя на данный момент я предполагаю, что это поведение соответствует стандартам).

Ответы [ 2 ]

11 голосов
/ 05 января 2011

При поиске в зависимости от аргумента (правильное имя для поиска koenig) компилятор добавляет к перегруженной функции набор функций, которые объявлены в пространствах имен каждого параметра.

В вашем случае первый operator<< объявлен в пространстве имен thesus::core,, которое является типом аргумента, с которым вы вызываете оператор. Поэтому это operator<< считается для ADL, потому что оно объявлено в связанном пространстве имен

Во втором случае operator<< кажется объявленным в глобальном пространстве имен, которое не является ассоциированным пространством имен, поскольку параметр один имеет тип из пространства имен std, а параметр 2 имеет тип из пространства имен theseus::core.

На самом деле, вероятно, ваш 2-й operator<< не объявлен в глобальном пространстве имен, так как это будет найдено при просмотре в родительских областях ... может быть, у вас есть что-то более похожее на это? Если вы можете разместить больше кода, мы можем дать лучший ответ.


Хорошо, я вспомнил, ADL не ищет в родительских областях, когда находит имя в текущей области. Таким образом, макрос повышения BOOST_TEST_MESSAGE расширяется и включает в себя operator<<, и в дереве областей действия есть некоторая нежизнеспособная operator<< между выражением и глобальной областью действия. Я обновил код, чтобы проиллюстрировать это (надеюсь).

#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;
    }  
}
1 голос
/ 05 января 2011

Перегрузка операторов похожа на функцию, но отличается, и одно из различий заключается в поиске пространства имен.

Как и функции, перегрузки операторов принадлежат пространству имен, но определение области действия функции было бы нецелесообразным.Представьте, что ваш код должен вызывать

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

. Поэтому оператор << должен находиться в пространстве имен одного из параметров, либо std (для cout), либо в пространстве имен p, в данном случае thesus::core.

Это принцип поиска Кенига.Вы должны определить перегрузку оператора в правильном пространстве имен.

...