以下代码无法编译:
#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/