Fur*_*ish 6 c++ overload-resolution c++-concepts c++20 std-ranges
我想<<为任何人写一个泛型,range最后我得到了这个:
std::ostream& operator << (std::ostream& out, std::ranges::range auto&& range) {
using namespace std::ranges;
if (empty(range)) {
return out << "[]";
}
auto current = begin(range);
out << '[' << *current;
while(++current != end(range)) {
out << ',' << *current;
}
return out << ']';
}
Run Code Online (Sandbox Code Playgroud)
像这样测试:
int main() {
std::vector<int> ints = {1, 2, 3, 4};
std::cout << ints << '\n';
}
Run Code Online (Sandbox Code Playgroud)
它完美运行并输出:
Run Code Online (Sandbox Code Playgroud)[1,2,3,4]
但是,当测试时:
int main() {
std::vector<int> empty = {};
std::cout << empty << '\n';
}
Run Code Online (Sandbox Code Playgroud)
它出乎意料地输出:
Run Code Online (Sandbox Code Playgroud)[[,], ]
用调试器运行这段代码,我得出的结论是空范围的问题是我们运行了return out << "[]";. 一些 C++ 魔法决定我的,刚刚写的,
std::ostream& operator << (std::ostream& out, std::ranges::range auto&& range);
Run Code Online (Sandbox Code Playgroud)
是更好的匹配,然后提供<ostream>,
template< class Traits >
basic_ostream<char,Traits>& operator<<( basic_ostream<char,Traits>& os,
const char* s );
Run Code Online (Sandbox Code Playgroud)
因此,它不是"[]"像我们以前看到的那样只是发送到输出流,而是递归返回自身,但"[]"作为range参数。
这是一个更好的匹配的原因是什么?与发送[和]单独发送相比,我能否以更优雅的方式解决此问题?
编辑:这似乎很可能是 GCC 10.1.0 中的一个错误,因为较新的版本拒绝了该代码。
我认为这不应该编译。让我们将示例稍微简化为:
template <typename T> struct basic_thing { };
using concrete_thing = basic_thing<char>;
template <typename T> concept C = true;
void f(concrete_thing, C auto&&); // #1
template <typename T> void f(basic_thing<T>, char const*); // #2
int main() {
f(concrete_thing{}, "");
}
Run Code Online (Sandbox Code Playgroud)
该basic_thing/concrete_thing模仿是怎么回事什么basic_ostream/ ostream。#1是您提供的重载,#2是标准库中的重载。
显然,这两种重载对于我们正在进行的调用都是可行的。哪一个更好?
那么,他们在两个参数(是的,无论是精确匹配char const*是完全匹配"",即使系统正在进行指针腐烂,看为什么指针衰减优先于推导的模板吗?)。所以转换序列无法区分。
这两个都是函数模板,所以无法区分。
两个函数模板都比另一个更专业 - 两个方向的推导都失败(char const*can't matchC auto&&和concrete_thingcan't match basic_thing<T>)。
“更受约束”部分仅适用于两种情况下模板参数设置相同的情况,这在此处不正确,因此该部分无关紧要。
而且......基本上就是这样,我们没有决胜局了。gcc 10.1 接受这个程序的事实是一个错误,gcc 10.2 不再这样做了。虽然 clang 现在确实如此,但我相信这是一个 clang 错误。MSVC 拒绝为模棱两可:Demo。
无论哪种方式,这里都有一个简单的解决方法,即写入[然后]作为单独的字符。
不管怎样,你可能不想写
std::ostream& operator << (std::ostream& out, std::ranges::range auto&& range);
Run Code Online (Sandbox Code Playgroud)
首先,因为要使其真正正常工作,您必须将其粘贴在 namespace 中std。相反,您想为任意范围编写一个包装器并使用它:
template <input_range V> requires view<V>
struct print_view : view_interface<print_view<V>> {
print_view() = default;
print_view(V v) : v(v) { }
auto begin() const { return std::ranges::begin(v); }
auto end() const { return std::ranges::end(v); }
V v;
};
template <range R>
print_view(R&& r) -> print_view<all_t<R>>;
Run Code Online (Sandbox Code Playgroud)
并定义您operator<<要打印的print_view. 这样,这只是有效,您不必处理这些问题。演示。
当然,out << *current;您可能希望有条件地将其包裹起来out << print_view{*current};以使其完全正确,但我会将其留作练习。