我有一个针对别名为std::variant
(A::Var
)的重载<<
,同时我在不同命名空间C::Wrapper
中定义了一个模板函数,该函数只是将其参数转发到std::ostream
。
我正在尝试从A
中定义的另一个函数A::Foo
中调用它,但这给我带来了编译器错误。以下是这个玩具示例。
#include <iostream>
#include <variant>
namespace C {
struct Wrapper {
template<typename T>
auto& operator<<(T&& v) {
std::cout << std::forward<T>(v);
return *this;
}
};
}
namespace A {
using Var = std::variant<bool, int>;
auto& operator<<(std::ostream& os, const A::Var& v) {
std::visit([&os](auto i) { os << i; }, v);
return os;
}
struct Foo {
void m() {
C::Wrapper wrap;
Var v{3};
wrap << "hi"; // works
wrap << v; // compiler error
}
};
}
int main() {
A::Foo a;
a.m();
}
g++ -std=c++17
出现以下错误:
main.cpp: In instantiation of ‘auto& C::Wrapper::operator<<(T&&) [with T = std::variant<bool, int>&]’:
main.cpp:27:11: required from here
main.cpp:8:15: error: no match for ‘operator<<’ (operand types are ‘std::ostream’ {aka ‘std::basic_ostream<char>’} and ‘std::variant<bool, int>’)
8 | std::cout << std::forward<T>(v);
... many candidate functions none including my overloaded << for A::Var
我原本期望它能够成功编译并在运行时打印出3
。我尝试将所有定义都放在类外,移除const
限定符,将重载的<<
函数设为全局,但这些方法都没有奏效。
我该如何修复这个错误,同时保留命名空间和类结构呢?
更新:
通过在全局命名空间中定义<<
,可以使代码在gcc下编译,但在clang下会失败:
namespace A {
using Var = std::variant<bool, int>;
}
auto &operator<<(std::ostream &os, const A::Var &v) {
std::visit([&os](auto i) { os << i; }, v);
return os;
}
namespace A {
struct Foo {
void m() {
C::Wrapper wrap;
A::Var v{3};
wrap << "hi"; // works
wrap << v; // compiler error
}
};
} // namespace A
clang++ -std=c++17
出现错误:
main.cpp:7:15: error: call to function 'operator<<' that is neither visible in the template definition nor found by argument-dependent lookup
std::cout << std::forward<T>(v);
^
main.cpp:28:10: note: in instantiation of function template specialization 'C::Wrapper::operator<<<std::variant<bool, int> &>' requested here
wrap << v; // compiler error
^
main.cpp:17:7: note: 'operator<<' should be declared prior to the call site
auto &operator<<(std::ostream &os, const A::Var &v) {
^
1 error generated.
为什么它在gcc下能工作,但是在clang下却不行?
Var
只是一个别名,而不是独立的类型。你正在调用C::Wrapper::operator<<(const std::variant<bool, int> &)
, 由于在该调用中没有引用namespace A
,因此 ADL 不知道要在namespace A
中查找重载的operator<<
。它只会考虑C::Wrapper
,namespace C
和全局命名空间中的运算符。 - Remy Lebeau重载,它们位于完全相同的命名空间中,该命名空间属于
X。对于
std::`类型的自定义无法正确执行。 - user17732522