我想做一个简单的
ostream& operator <<
重载,但如果碰巧有另一个重载,我不希望编译器出现任何歧义错误。
这是我能想到的最接近的事情:
template<class Collection>
std::ostream& operator << (std::ostream& stream, const Collection& other)
requires requires () {
requires not requires() {stream << other;};//errors here
other.begin() != other.end();
++other.begin();
stream << *other.begin();
} {...}
上面代码的问题是编译器产生以下错误:
1. Satisfaction of constraint 'requires { requires !requires { stream << other; }; }' depends on itself [constraint_depends_on_self]
我想要的是为任何集合创建一个通用重载,以便轻松输出到控制台(用于调试目的),但是像 std::string 这样的某些集合已经有重载,因此对
std::ostream << std::string
之类的调用是不明确的.
缓解该问题的一个简单方法是专门禁止
std::string
的过载,但我很好奇是否可以为所有现有的过载制定解决方案,而不仅仅是一个特定的过载。
我正在使用 C++20(我不介意 C++23 解决方案)clang 和 gcc 编译器,如果这很重要的话。
附注请不要建议使用包装器或不同名称的函数/运算符。这不是解决问题,这是在逃避问题。
最简单的方法是定义两个概念:
然后你必须编写模板并要求满足约束
Iterable<T> && !Streamable<T>
。
例如:
template <typename T>
concept Iterable = requires(T t)
{
t.cbegin();
t.cend();
};
template <typename T>
concept Streamable = requires(std::ostream & os, T t)
{
os << t;
};
template <typename Collection>
requires (Iterable<Collection> && not Streamable<Collection>)
std::ostream & operator<<(std::ostream & os, const Collection & c)
{
os << '{';
for(auto cit = c.cbegin(); cit < c.cend(); ++cit)
{
if(cit != c.cbegin())
os << ", ";
os << *cit;
}
os << "}\n";
return os;
}
这样,您的重载将仅用于尚未提供运算符的可迭代类型。