Почему я не могу поместить эту перегрузку оператора в то же пространство имен, что и структура?

у меня есть следующий код:

#include <iostream>
#include <vector>
namespace X {
    std::ostream& operator<<(std::ostream& os,const std::vector<double>& v){
        for (int i=0;i<v.size();i++){os << v[i] << " ";}
        return os;
    }
    namespace Y {
        struct A {std::vector<double> x;};    
        std::ostream& operator<<(std::ostream& os,const A& a){
            os << a.x << std::endl;
            return os;
        }
   }     
}

using namespace X;

int main(int argc, char** argv) {
   std::vector<double> v(10,0);
   std::cout << v << std::endl;
   Y::A a;
   std::cout << a << std::endl;
   return 0;
}

первая перегрузка работает, но второй нет. По какой-то причине он не может найти первого. Я получаю сообщение об ошибке:

no match for 'operator<<' (operand types are 'std::ostream 
{aka std::basic_ostream<char>}' and 'const std::vector<double>')
     os << a.x << std::endl;
        ^

Я не понимаю, почему я получаю эту ошибку. Например что-то вроде этого, кажется, вполне уважительные:

namespace A {
    void foo(){}
    namespace B {
        void bar(){foo();}
    }
}

однако единственным способом исправить вышеуказанную проблему было поместить вторую перегрузку также В X. Почему невозможно иметь ее в том же пространстве имен, что и структура (т. е. X:: Y)?

PS: Я читал о ADL и я нашел некоторые связанные с этим вопросы (например,этой и этой, но то,что я понял из прочтения этого, должно работать.

3 ответов


в Argument Depended Lookup (или Koenig Lookup) компилятор добавляет в область видимости все символы, объявленные в родительских областях каждого параметр.

даже если Y - это "пространство ребенка"X, они не связаны с точки зрения ADL. Первый из ваших параметров-тип, определенный в std:: пространство имен, а второй-локальный символ (определяется в том же пространстве имен, что и сама функция).

обратите внимание, что из-за причин, упомянутых выше, вы скорее всего получите еще одну ошибку в этой строке:

std::cout << v << std::endl;

когда компилятор не сможет найти operator<< перегружены для std::vector<double> (потому что он находится внутри namespace X).

чтобы решить эту проблему, вы можете использовать:

using X::operator<<

внутри namespace Y или переместить эту перегрузку.

Если вам интересно, почему foobar пример работает: потому что ADL (аргумент зависимый поиск) - это область параметров функций, а не сама функция. В foobar код ADL не применяется.


согласно другим ответам, я в конце концов пришел к выводу, что ADL оператора

сегодняшний урок: всегда пишите оператор

вот исправление:

#include <iostream>
#include <vector>


namespace X 
{
    std::ostream& operator<<(std::ostream& os,const std::vector<double>& v){
        for (int i=0;i<v.size();i++){os << v[i] << " ";}
        return os;
    }
    namespace Y 
    {
        struct A 
        { 
            std::vector<double> x;
            void write(std::ostream&os) const {
                os << x << std::endl;
            }
        };    
        std::ostream& operator<<(std::ostream& os,const A& a)
        {
            a.write(os);
            return os;
        }
    }     
}

using namespace X;

int main(int argc, char** argv) 
{
   std::vector<double> v(10,0);
   std::cout << v << std::endl;
   X::Y::A a;
   std::cout << a << std::endl;
   return 0;
}

так просто:чтобы перегрузить функцию, перегруженная версия должна жить в том же nemaspace, иначе это совершенно другая функция. имя функции (для компилятора) - это полный путь от глобального пространства имен для самой функции.

::function_at_global_namespace();
Namespace::function_name();      // Some funtion within a namespace;
Namespace_1::function_name();    // Some other function within another namespace;

и

Standar std::ostream& operator<< проживает в std пространство имен, вы не перегружаете этот оператор, просто определяя anotherone в пространстве имен X.

как указано @0x499602D2 вы должны использовать X::operator<< в пространстве имен Y для того, чтобы вызвать эту версию оператора.

std::ostream& std::operator<< и std::ostream& X::operator<< различные функции.

в следующем коде ни одна из версий foo не перегружается.

// What version of foo gets called?  A::foo, or B::foo?
namespace A {
    void foo(){cout << "A::foo" << endl;}
    namespace B {
        void foo(){ cout << "B::foo" << endl;}
        void bar(){foo();}
    }
}

namespace C { void foo(int a) { cout << "C:foo" << endl; } }