перегрузка потокового оператора для имен вложенных типов внутри шаблонов - PullRequest
0 голосов
/ 05 декабря 2018

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

#include <type_traits>
#include <optional>
#include <iostream>

using namespace std;

namespace dbj {

    template< typename K, typename V >
    class top final {
        static_assert(
          ! is_same_v<K,V>, 
          " to make things simpler K and V must be different types"
        );
    public:
        // nested type names
        using key_type = optional<K>;
        using val_type = optional<V>;
        using type = top;

    private:
        key_type key_{};
        val_type val_{};
    public:
        top() = delete;
        explicit top(K k, V v) : key_(k), val_(v) {}

    private:    
        // PROBLEM A: not found by 
        // friend wostream & operator << (wostream & os, type top_) 
        friend wostream & operator << (wostream & os, key_type key_arg_ ) {
            return os << L"\nK  : " << key_arg_.value_or(K{});
        }

        // PROBLEM B: not found by
        // friend wostream & operator << (wostream & os, type top_) 
        friend wostream & operator << ( wostream & os, val_type val_arg_ ) {
            return os << L"\nV  : " << val_arg_.value_or(V{});
        }

        // found no problem
        friend wostream & operator << (wostream & os, type top_) 
        {
            // ISSUE D: this is not looking for overloads in the immediate scope
            // i.e. inside the template class
            // this is first looking for operator declaration inside namespace dbj
            return os << L"\n\nprinting dbj::top<K,V> : " << top_.key_ << L"," << top_.val_;
        }

    }; // top
} // dbj ns

using top_type = dbj::top<wstring, int>;

extern "C"  int test_operator_overloading_puzzle()
{
    top_type top_{ L"the key", 42 };
    std::wcout << top_ << std::endl;
    return 1;
}

Также доступно здесь: https://wandbox.org/permlink/jMKpn6CKFL2cyceO

Каждый компилятор жалуется, что в операторе потоковой передачи для type top_ не найдено совпадений дляпотоковая передача top_.key (помечена ВЫПУСК D в приведенном выше коде).Почему поиск не может найти две потоковые функции, которые я объявил прямо над ними?

Ответы [ 2 ]

0 голосов
/ 07 декабря 2018

Просто опираясь на упрощение вопроса Барри, а затем поверх его ответа.

namespace {

    using namespace std;

    namespace A {
        struct X { 
            // #1 --> SOLUTION to problem #1 
            // friend operator is here in the scope where
            // name lookup can find it, declared and defined
            friend wostream& operator<<(wostream& os, X) {
                return os << L"A::X" ;
            }
        };
    }

    namespace B {
        struct Y {
            A::X x;

            // #1 --> PROBLEM: name lookup can not find this 
            // as it is in the scope unrelated to A::X
            /*
            friend std::ostream& operator<<(std::ostream& os, A::X) {
                return os;
            }
            */

            // #2
            friend wostream& operator<<(wostream& os, Y y) {
                // << y.x is normally found inside the 
                // type of x and that is A::X
                return os << L"\nB::Y --> " << y.x; 
            }
        };
    }

    void test_barry() {
        B::Y by_;

        wcout << by_;
    }
}

Это для ситуаций, когда мы можем вставить необходимый оператор друга внутри вложенного типа.В противном случае, пожалуйста, смотрите ответ Барри.

Возможно, для дальнейшего образования это хорошее начало .

0 голосов
/ 06 декабря 2018

Вот более простое воспроизведение:

namespace A {
  struct X { };
}

namespace B {
  struct Y {
    A::X x;

    // #1
    friend std::ostream& operator<<(std::ostream& os, A::X) {
      return os;
    }

    // #2
    friend std::ostream& operator<<(std::ostream& os, Y y) {
      return os << y.x; // error: no match for operator<<
    }
  };
}

Это из-за того, как работает поиск по имени.Когда вы объявляете и определяете функцию-друга области имен, как эта, эта функция может только быть найдена путем зависимого от аргумента поиска ее аргументов.Он никогда не обнаруживается при обычном неквалифицированном поиске.

Но функция, которую вы объявляете в #1, фактически не находится в связанном пространстве имен ни одного из своих аргументов - функция объявлена ​​в namespace B, нодва аргумента в namespace std и namespace A соответственно.В результате, когда мы пишем os << y.x, при обычном неквалифицированном поиске не найдено ни одного подходящего кандидата, а затем не найдено ни одного кандидата в зависимости от аргумента - #1 не находится в правильном пространстве имен.Следовательно, нет кандидатов.

Самое короткое решение - просто добавить объявление области имен пространства #1 вне struct Y:

namespace B {
  std::ostream& operator<<(std::ostream&, A::X);

  struct Y { ... };
}

Теперь эта функция может быть найденаобычный неквалифицированный поиск, поэтому вызов в #2 работает.Но на самом деле, нет никакой причины объявлять #1 как функцию друга с B::Y (это никоим образом не относится к B::Y), так что просто объявите это внешне.Он также не очень хорошо работает как потоковый оператор, поэтому, вероятно, просто сделайте его обычной функцией.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...