c++ - 模板内嵌套类型名称的流运算符重载

标签 c++ c++17

以下代码无法编译:

#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 的匹配项(在上面的代码中标记为 ISSUE D)。为什么查找找不到我直接在它们上面声明的两个流函数?

最佳答案

这是一个更简单的复制:

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 的 namespace 范围声明在struct Y之外:

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

  struct Y { ... };
}

现在,这个函数可以通过常规的非限定查找找到,所以#2中的调用作品。但实际上,没有理由声明 #1作为 friend 功能 B::Y (它绝不是指 B::Y ),所以只需在外部声明即可。它也不能很好地作为流媒体运营商,所以可能只是让它成为一个常规功能。

关于c++ - 模板内嵌套类型名称的流运算符重载,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/53638922/

相关文章:

c++ - boost.asio 测试问题

c++ - CLion:构建程序不会在 cmd 中运行

c++ - 从 r 值引用限定方法返回 r 值引用是一个好习惯吗?

c++ - 如何在 Linux CentOS 上更改默认 GCC 编译器以与 MPI 一起使用

c++ - 什么样的 .lib 文件以 "!<arch>"开头?

c++ - 如何使用线程每 0.3 秒发送一次消息?

c++ - 与 [[maybe_unused]] 的结构化绑定(bind)

c++ - 错误 : call of overloaded 'stoi(nlohmann::basic_json<>::value_type&)' is ambiguous

c++ - 使用 ? 返回可选值: operator

c++ - 无法使用 std::variant 的重载运算符 <<() 流式传输 std::endl